Прошу прощения за длинный вопрос, но я чувствую, что необходимо включить всю эту информацию.Использование интерфейса обратного вызова как DependencyProperty в WPF?
До сих пор я использовал возможно-неортодоксальный способ добавления UserControls в свои приложения. Предположим, что у меня есть UserControl с именем Diagnostics
, у которого есть кнопка, которая при щелчке выполняет функцию, специфичную для приложения, которое ее владеет. Например, если я отменил Диагностику в AppA, я хочу, чтобы она отображала «A», и если я отброшу ее в AppB, я хочу, чтобы AppB определял поведение, чтобы он отображал «B».
Обычно я реализую это через интерфейс обратного вызова, который передается конструктору UserControl, что довольно просто. Вот некоторые примеры «код», который, вероятно, не будет компилироваться, но представлена только уточнить, что я в основном делали раньше, и то, что я пытаюсь сделать:
public interface IDiagnosticsCallback {
void DisplayDiagnostics(); // implemented by owner of Diagnostics UserControl
}
public class MyApp : IDiagnosticsCallback {
public void DisplayDiagnostics() {
MessageBox.Show("Diagnostics displayed specifically for MyApp here");
}
}
public Diagnostics : UserControl {
private IDiagnosticsCallback _callback { get; private set; }
public Diagnostics(IDiagnosticsCallback callback) {
_callback = callback;
}
public void ShowDiagnostics_Click(object sender, EventArgs e) {
_callback.DisplayDiagnostics();
}
}
проблема, которую я имел в прошлом был понимание того, как объявить UserControl, который принимает параметр в своем конструкторе (т. е. не имеет конструктора по умолчанию) в XAML, и, по-видимому, вы не можете. Я работал над этим с довольно-неэлегантным методом - я бы дал главной панели имя в XAML, а затем из кода-кода я бы создал Diagnostics, передав ему необходимый обратный вызов, а затем добавлю диагностику в список панели детей. Гросс и нарушает использование MVVM, но он работает.
В этот уик-энд я решил попробовать узнать, как это сделать для класса и TextBox, и оказалось, что все, что мне нужно было сделать, это создать DependencyProperty
в моем UserControl и использовать привязку данных. Это выглядит что-то вроде этого:
public ClassA
{
public void ShowSomethingSpecial()
{
MessageBox.Show("Watch me dance!");
}
}
public MyApp
{
public ClassA Foo { get; set; }
}
public Diagnostics : UserControl
{
public static readonly DependencyProperty SomethingProperty = DependencyProperty.Register("Something", typeof(ClassA), typeof(Diagnostics), new PropertyMetadata());
public ClassA Something
{
get { return (MyApp)GetValue(SomethingProperty); }
set { SetValue(SomethingProperty, value); }
}
// now uses default constructor
public void ShowSomethingSpecial_Click(object sender, EventArgs e)
{
Something.ShowSomethingSpecial();
}
}
MyApp.xaml
<diags:Diagnostics Something="{Binding Foo}" />
Так Foo является собственностью MyApp, которая DataBound на что-то DependencyProperty Диагностики. Когда я нажимаю кнопку в UserControl, поведение определяется ClassA. Гораздо лучше, и работает с MVVM!
Теперь я хотел бы сделать еще один шаг и вместо этого передать интерфейс обратного вызова в свой UserControl, чтобы он мог получать состояния моих цифровых входов и выходов. Я ищу что-то вроде этого:
public Diagnostics : UserControl
{
public interface IDioCallback
{
short ReadInputs();
short ReadOutputs();
void SetOutput(char bit);
}
public IDioCallback DioCallbackInterface {
get { return (IDioCallback)GetValue(DioCallbackInterfaceProperty); }
set { SetValue(DioCallbackInterfaceProperty,value); }
}
// Using a DependencyProperty as the backing store for DioCallbackInterface. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DioCallbackInterfaceProperty = DependencyProperty.Register("DioCallbackInterface",typeof(IDioCallback),typeof(Diagnostics),new PropertyMetadata(0)); // PropertyMetadata is the problem...
}
public class DIO : IDioCallback
{
public short ReadInputs() { return 0; }
public short ReadOutputs() { return 0; }
public void SetOutput(char bit) {}
}
public class MyApp
{
public DIO MyDIO { get; set; }
}
MyApp.xaml
<diags:Diagnostics DioCallbackInterface="{Binding MyDIO}" />
Хотя мой код (возможно не точный код выше, но мой реальный проект) компилируется успешно, оказывается, что PropertyMetadata передается Регистрация виноват. Я получаю исключение, которое говорит: «Тип значения по умолчанию не соответствует типу свойства« DioCallbackInterface ».»
Я делаю что-то действительно неортодоксальное, или этот подход к интерфейсам привязки данных действительно возможен? Если нет, то какие рекомендуемые способы определения поведения UserControl на основе приложения, в котором он используется?
Спасибо за ответ! Казалось бы, что я хочу сделать, это невозможно, так как я хочу передать интерфейс. Я работал над этим временно, перейдя в производный класс, но это не то, что я действительно хочу. Я использую RelayCommand уже несколько лет и считаю это незаменимым. Мой конкретный вопрос заключается не в правильном способе обработки команд, а в том, как получить дочерние элементы управления интерфейсом обратного вызова через XAML, чтобы при нажатии кнопок они могли выполнять определенную операцию, определенную классом, реализующим интерфейс. – Dave
ICommand - это интерфейс. Можете ли вы передать экземпляр ICommand на элемент управления Button? Я думаю да. Таким же образом вы можете передать экземпляр вашего интерфейса в UserControl. Все, что вам нужно исправить в вашем примере кода, это использовать 'new PropertyMetadata (null)'. – stukselbax
Спасибо, я использовал новый PropertyMetadata (null) и предотвратил сбой. Решение, с которым я пошел, в конце концов состояло в том, чтобы иметь ссылки на мои интерфейсы обратного вызова в модели, которые я использовал для DependencyProperty, и это, казалось, работало достаточно хорошо. – Dave