Обновлено (2018) с 2 версиями (проверено и продолжает работать и в Woocommerce 3.3.x) :
blockquote>1) Версия плагина с Функция конструктора:
Используемые вами крючки устарели в WooCommerce 3 +
Чтобы заставить работать для всех цен на продукты, включая цены на изменение, вы должны использовать это:
## The following goes inside the constructor ## // Simple, grouped and external products add_filter('woocommerce_product_get_price', array( $this, 'custom_price' ), 99, 2 ); add_filter('woocommerce_product_get_regular_price', array( $this, 'custom_price' ), 99, 2 ); // Variations add_filter('woocommerce_product_variation_get_regular_price', array( $this, 'custom_price' ), 99, 2 ); add_filter('woocommerce_product_variation_get_price', array( $this, 'custom_price' ), 99, 2 ); // Variable (price range) add_filter('woocommerce_variation_prices_price', array( $this, 'custom_variable_price' ), 99, 3 ); add_filter('woocommerce_variation_prices_regular_price', array( $this, 'custom_variable_price' ), 99, 3 ); ## This goes outside the constructor ## public function custom_price( $price, $product ) { // Delete product cached price (if needed) // wc_delete_product_transients($product->get_id()); return $price * 2; // X2 for testing } public function custom_variable_price( $price, $variation, $product ) { // Delete product cached price (if needed) // wc_delete_product_transients($variation->get_id()); return $price * 2; // X2 for testing }
Код проверен и отлично работает (только) в WooCommerce 3 +.
2) Для версии темы:
functions.php
файл в активной дочерней теме (или активной теме) :// Simple, grouped and external products add_filter('woocommerce_product_get_price', 'custom_price', 99, 2 ); add_filter('woocommerce_product_get_regular_price', 'custom_price', 99, 2 ); // Variations add_filter('woocommerce_product_variation_get_regular_price', 'custom_price', 99, 2 ); add_filter('woocommerce_product_variation_get_price', 'custom_price', 99, 2 ); function custom_price( $price, $product ) { // Delete product cached price (if needed) // wc_delete_product_transients($product->get_id()); return $price * 2; // X2 for testing } // Variable (price range) add_filter('woocommerce_variation_prices_price', 'custom_variable_price', 99, 3 ); add_filter('woocommerce_variation_prices_regular_price', 'custom_variable_price', 99, 3 ); function custom_variable_price( $price, $variation, $product ) { // Delete product cached price (if needed) // wc_delete_product_transients($variation->get_id()); return $price * 2; // X2 for testing }
Протестировано и работает в woocommerce 3 +
Для продаваемых товаров у вас есть эти крючки:
woocommerce_product_get_sale_price
(простые, сгруппированные и внешние продукты)woocommerce_variation_prices_sale_price
(переменные продукты (min-max))woocommerce_variation_prices_sale_price
(варианты продуктов)
Это обычная проблема с началом работы с людьми. Всякий раз, когда вы обновляете элементы пользовательского интерфейса из потока, отличного от основного потока, вам нужно использовать:
this.Dispatcher.Invoke(() =>
{
...// your code here.
});
Вы также можете использовать control.Dispatcher.CheckAccess()
, чтобы проверить, является ли текущий поток владеет контролем. Если он действительно владеет, ваш код выглядит нормально. В противном случае используйте шаблон выше.
это произошло со мной, потому что я попытался выполнить access UI
компонент в another thread insted of UI thread
, как этот
private void button_Click(object sender, RoutedEventArgs e)
{
new Thread(SyncProcces).Start();
}
private void SyncProcces()
{
string val1 = null, val2 = null;
//here is the problem
val1 = textBox1.Text;//access UI in another thread
val2 = textBox2.Text;//access UI in another thread
localStore = new LocalStore(val1);
remoteStore = new RemoteStore(val2);
}
, чтобы решить эту проблему, обернуть любой вызов ui внутри , о чем упоминал Кандид в своем ответе
private void SyncProcces()
{
string val1 = null, val2 = null;
this.Dispatcher.Invoke((Action)(() =>
{//this refer to form in WPF application
val1 = textBox.Text;
val2 = textBox_Copy.Text;
}));
localStore = new LocalStore(val1);
remoteStore = new RemoteStore(val2 );
}
Кроме того, другое решение гарантирует, что ваши элементы управления создаются в потоке пользовательского интерфейса, а не потоком рабочего потока, например.
Чтобы добавить мои 2 цента, исключение может возникнуть, даже если вы вызываете свой код через System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()
. Дело в том, что вам нужно вызвать Invoke()
элемента Dispatcher
элемента управления, к которому вы пытаетесь получить доступ, что в некоторых случаях может не совпадать с System.Windows.Threading.Dispatcher.CurrentDispatcher
. Поэтому вместо этого вы должны использовать YourControl.Dispatcher.Invoke()
для обеспечения безопасности. Я уже несколько часов стучал головой, прежде чем понял это.
Проблема в том, что вы вызываете GetGridData
из фонового потока. Этот метод обращается к нескольким элементам управления WPF, которые привязаны к основному потоку. Любая попытка доступа к ним из фонового потока приведет к этой ошибке.
Чтобы вернуться к правильной теме, вы должны использовать SynchronizationContext.Current.Post
. Однако в этом конкретном случае, похоже, большая часть работы, которую вы выполняете, основана на пользовательском интерфейсе. Следовательно, вы создадите фоновый поток, чтобы сразу вернуться к потоку пользовательского интерфейса и выполнить некоторую работу. Вам нужно немного реорганизовать свой код, чтобы он мог выполнять дорогостоящую работу в фоновом потоке, а затем публиковать новые данные в потоке пользовательского интерфейса после
Как уже упоминалось здесь , Dispatcher.Invoke
может заморозить UI. Вместо этого используйте Dispatcher.BeginInvoke
.
Вот удобный класс расширений, который упрощает проверку и вызов диспетчера вызовов.
Пример использования: (вызов из окна WPF)
this Dispatcher.InvokeIfRequired(new Action(() =>
{
logTextbox.AppendText(message);
logTextbox.ScrollToEnd();
}));
Класс расширения:
using System;
using System.Windows.Threading;
namespace WpfUtility
{
public static class DispatcherExtension
{
public static void InvokeIfRequired(this Dispatcher dispatcher, Action action)
{
if (dispatcher == null)
{
return;
}
if (!dispatcher.CheckAccess())
{
dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle);
return;
}
action();
}
}
}
Еще одно полезное использование для Dispatcher.Invoke
- это немедленное обновление пользовательского интерфейса в функции, выполняющей другие задачи:
// Force WPF to render UI changes immediately with this magic line of code...
Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle);
Я использую это, чтобы обновить текст кнопки до « Обработка ... "и отключить его при выполнении запросов WebClient
.
Я также обнаружил, что System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()
не всегда является диспетчером целевого контроля, точно так же, как dotNet пишет в своем ответе. У меня не было доступа к диспетчеру управления, поэтому я использовал Application.Current.Dispatcher
, и он решил проблему.
По какой-то причине ответ Кандиде не строился. Это было полезно, однако, поскольку это заставило меня найти это, которое отлично работало:
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke((Action)(() =>
{
//your code here...
}));
Это работает для меня.
new Thread(() =>
{
Thread.CurrentThread.IsBackground = false;
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, (SendOrPostCallback)delegate {
//Your Code here.
}, null);
}).Start();
Вам нужно обновить пользовательский интерфейс, поэтому используйте
Dispatcher.BeginInvoke(new Action(() => {GetGridData(null, 0)}));
Я продолжал получать ошибку, когда добавлял каскадные comboboxes в свое приложение WPF и разрешал ошибку с помощью этого API:
using System.Windows.Data;
private readonly object _lock = new object();
private CustomObservableCollection<string> _myUiBoundProperty;
public CustomObservableCollection<string> MyUiBoundProperty
{
get { return _myUiBoundProperty; }
set
{
if (value == _myUiBoundProperty) return;
_myUiBoundProperty = value;
NotifyPropertyChanged(nameof(MyUiBoundProperty));
}
}
public MyViewModelCtor(INavigationService navigationService)
{
// Other code...
BindingOperations.EnableCollectionSynchronization(AvailableDefectSubCategories, _lock );
}