Using callback interface as a DependencyProperty in WPF? - wpf

I apologize for the lengthy question, but I feel like it is necessary to include all of this information.
Until now, I've been using a possibly-unorthodox way of adding UserControls to my applications. Let's say I have a UserControl called Diagnostics that has a button, that when clicked, performs a function that is specific to the application that owns it. For example, if I drop Diagnostics into AppA, I want it to display "A", and if I drop it into AppB, I want AppB to define the behavior so it displays "B".
I typically implement this via a callback interface that is passed to the UserControl's constructor, which is pretty straightforward. Here's some sample "code" that probably won't compile, but is presented just to clarify what I've basically done before, and what I am trying to do:
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();
}
}
The problem I had in the past was understanding how to declare a UserControl that takes a parameter in its constructor (i.e. doesn't have a default constructor) in XAML, and apparently you can't. I worked around this with a fairly-inelegant method -- I would give the main panel a name in XAML, and then from code-behind I would create Diagnostics, passing it the necessary callback, and then I would add Diagnostics to the panel's list of children. Gross and violates usage of MVVM, but it works.
This weekend, I decided to try to learn how to do it for a class and a TextBox, and it turns out that all I had to do was to create a DependencyProperty in my UserControl and use databinding. It looks something like this:
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}" />
So Foo is a property of MyApp, which is databound to the Something DependencyProperty of Diagnostics. When I click the button in the UserControl, the behavior is defined by ClassA. Much better, and works with MVVM!
What I'd like to do now is to go one step further and instead pass a callback interface to my UserControl so that it can get the states of my digital inputs and outputs. I'm looking for something like this:
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}" />
While my code (maybe not the exact code above, but my real project) does compile successfully, it appears that the PropertyMetadata passed to Register is at fault. I get an exception that says "Default value type does not match type of property 'DioCallbackInterface'."
Am I doing something really unorthodox, or is this approach to databinding interfaces actually possible? If not, what are the recommended ways of defining how a UserControl behaves based on the application it's being used in?

The exception you have mentioned because of this:
new PropertyMetadata(0)
You have passed 0 (of type Int32) instead of the null or whatever you like for your interface: IDioCallback.
I cannot say that the way you select is wrong, but you should keep in mind that every user of your UserControl must implement that interface you have defined. If you have several properties that you would like to pass to the UserControl, you can basically discard them via DependencyProperty.
In your case you also would like to inject some logic to the UserControl Button. Let me suppose that this control has only one button. MVVM-way to handle Button.Click event is done via ICommand - you can declare the command property in your ViewModel and use it as data source for data binding in your UserControl as DependencyProperty, passing it properly to the Button.
Also you can have an agreement with all of your data context, and use special name for that property. For example:
public interface IViewModelWithCommand
{
public ICommand TheCommand { get; }
}
Implement it for each data context you need, and use TheCommand property name inside your data template of your UserControl. In the code-behind you can create type validation of DataContext passed to your UserControl, and throw an exception in case the type is not implements your interface
Here several articles you probably should be interested in:
RelayCommand
Commands, RelayCommands and EventToCommand
How to use RelayCommands
Using RelayCommand will simplify your life because you don't need to re-implement interface for every command, instead, you need to pass valid action that you want.

Related

Static Property Bind to Grid in wpf

I have a lot of static poperties in my class library. I want to bind the property values in grid with two way binding. How can bind it?
public class AllStaticProperty
{
public static int JA{get;set;}
public static float JB{get;set;}
public static bool JC{get;set;}
public static int[] JD=new int[1000];
//More properties here
public static float[] ZZ=new float[2000];
}
I want bind static property name grid first column field and user enter the property value in grid second column then back to store the value in static property. how can I bind( I have more than 3000 static property)
If you want to bind properties and reflect changes to them in the user interface, you have to implement INotifyPropertyChanged in the corresponding class and raise the PropertyChanged event whenever a property changes its value to trigger binding updates in controls to get the latest value.
However, static properties cannot access instance methods, so how should they raise property changed notifications? There are ways to achieve this, as you can see in this related post, but it is bad design. Another issue here is to bind a static property of a non-static class two-ways, but there are also workarounds.
I recommend to overthink your design and create view models that implement INotifyPropertyChanged, e.g.:
public class SampleViewModel : INotifyPropertyChanged
{
private int _ja;
public int JA
{
get => _ja;
set
{
if (_ja == value)
return;
_ja = value;
OnPropertyChanged();
}
}
// ...other properties and backing fields.
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Then you can bind a property with the usual syntax and add Mode=TwoWay if that is not the default already.
SomeProperty="{Binding JA, Mode=TwoWay}"
An alternative option is to create one or more wrapper view models that are implemented as above, but access the static properties of your AllStaticProperty class, but then you need to synchronize the view model with the static properties, too, if any static property changes.

WPF Dependency Object

Has anyone ever heard of implementing IDependencyObject instead of inheriting from it -- that way one could actually create a class hierarchy instead of having to use only interfaces when trying to get both dependency object/property and custom behavior on our classes.
I want to have a hierarchy of class kinds that are directly usable in the context of an existing structure, i.e. Polygon. I want to be able to use my PolyType in any place, and without any more dialogue and indirection that would be required if I place the PolyGon existing type as a Part of my DependencyObject. But I also want to be able to have my class as the a) the target of {Binding} markup extension, b) Animate properties of PolyType and c) apply themed styling to PolyType.
I want to implement IDependencyObject instead of being forced to inherit from it directly, and obstructing my ability to be a direct descendent and usable in place of, PolyGon.
Not sure why you have to inherit from DependencyObject. I use a custom code snippet that generates the following code to register a dependancy property:
public partial class UserControl1 : UserControl
{
public static DependencyProperty MyPropertyProperty = DependencyProperty.Register("MyProperty", typeof(Polygon), typeof(UserControl1), new FrameworkPropertyMetadata(new PropertyChangedCallback(MyProperty_Changed)));
public Polygon MyProperty
{
get { return (Polygon)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
private static void MyProperty_Changed(DependencyObject o, DependencyPropertyChangedEventArgs args)
{
UserControl1 thisClass = (UserControl1)o;
thisClass.SetMyProperty();
}
private void SetMyProperty()
{
//Put Instance MyProperty Property Changed code here
}
public UserControl1()
{
InitializeComponent();
}
}
As you can see the DependencyObject can be any type of object. If this is not what you need, please post you code examples, or explain your situation better.

Bind Textbox Text to a Static Property

I have a Static Class with the following Static Property:
public static class PrintingMethods
{
public static String DocsCountString
{
get
{
return printDocuments.Count.ToString();
}
}}
I have a text box that I bind to this property:
<TextBlock Text="{x:Static my:PrintingMethods.DocsCountString}" x:Name="PagesNumber"/>
This works - I can see the number in the Text, But it never changes If the Property Value Change.
I am quite new to this, I know there are things like Dependency Object and INotify Interface but this won't work for Static.
If anyone can help me with a working code (modification to what I wrote) to Achieve real time textChange that would be great, Thanks!!!
Answer on our comments:
If you use the Singleton Pattern, you can bind to it like that
public sealed class MySingleton : INotifyPropertyChanged
{
public void RaiseProperty(string aPropName)
{
// implementation of INotifyPropertyChanged
}
public static MySingleton Instance
{
get{ return sInstance; }
}
public string MyProperty
{
get {return mMyProperty;}
set {mMyProperty = value; RaiseProperty("MyProperty"); }
}
private string mMyProperty;
private static MySingleton sInstance = new MySingleton();
}
As you can see you can easily use the INotifyPropertyChanged interface and implementation with a singleton class. You might want to make the constructor private to disallow creating another instance of this class. Also it would be possible to lazy allocate the MySingleton instance. You will find much more about singletons on stackoverflow.
<TextBlock Text="{Binding Source={x:Static my:MySingleton.Instance}, Path=MyProperty}"/>
The important part here now is the Binding and the overriden Source. Usually Binding takes the current DataContext. By setting a new Source the DataContext is irrelevant and the new Source is used to get the value behind the Path property.
you should use function in modifier is internal like:
Form2:
internal string foo()
{
return nom;
}
Form1:
form2 win= new form2();
win.ShowDialog();
Textbox.Text = win.foo();

MvvM with custom View-Elements. Data Binding problems

As a newbie in Data-Binding, I don't know what I am doing wrong.
I have some GUI elements defined in XAML, and I have data-binded them with appropriate ViewModels. So far so good.
I also have some custom elements (geometrical shapes) that I place inside a Canvas (which Canvas I place inside the mainwindow through a user-control). I derived these entities from FrameworkElement, to have support for data-binding.
So what I have done is to register some DependencyProperties and set the bindings to one of the existing ViewModels, as it seemed to me logical.
Now the DependencyProperties of these custom classes, display some strange behaviour.
i) When I keep the focus only on the Views (controls) that use the same ViewModel with the custom elements, the properties update normally. If I click everywhere else, the bindings break.
ii) Sometimes, the Callback wasn't called although the property was changing.
iii) When the StartupURI in App.xaml was the MainWindow, if I declared the ProfileV as a field (no matter where it was instantiated), the databinding mechanism worked in the way of (i). If it was declared inside the constructor, the mechanism didn't worked.
What I am doing wrong, and what crucial thing i misunderstand about databinding ??
class ProfileV : FrameworkElement, IGraphicalElement
{
public int SelectedTab
{
get { return (int)GetValue(SelectedTabProperty); }
set { SetValue(SelectedTabProperty, value); }
}
public static readonly DependencyProperty SelectedTabProperty =
DependencyProperty.Register("SelectedTab", typeof(int), typeof(ProfileV),
new PropertyMetadata(new PropertyChangedCallback(CallBack)));
public ProfileV(GeneralExecutionVM VM,CanvasV canvasV)
{
DataContext = VM;
BindingOperations.SetBinding(this, SelectedTabProperty, new Binding("SelectedTab"));
}
public static void CallBack(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
}
Which binds with this
public class GeneralExecutionVM : ObservableObject
{
private int _SelectedTab;
public int SelectedTab
{
get { return _SelectedTab; }
set
{
if (_SelectedTab == value) return;
_SelectedTab = value;
base.RaisePropertyChanged("SelectedTab");
}
}
}
(Observable Object, is the base class from the MVVM Foundation, of Josh Smith.)
ANSWERED
OK i found it. The misconception here is about the DataContext.
Be careful when and where you set it, against setting an explicit source object. I misused it here, and caused a small chaotic situation.

Using dependency properties in wpf

I'm not quite sure if I've got the right grasp on this or not, what I've read seems to agree with what I'm trying to do, however It doesn't seem to be working.
If I add an additional owner to a dependency property of a class, whenever the orig class dp changes, the change should get propagated to the additional owner, correct?
What I have is a custom control, which I want to set a property on, and then on certain objects that are within the custom control data template inherit this property value.
public class Class1: DependencyObject{
public static readonly DependencyProperty LongDayHeadersProperty;
public bool LongDayHeaders {
get { return (bool)GetValue(LongDayHeadersProperty); }
set { SetValue(LongDayHeadersProperty, value); }
}
static Class1(){
LongDayHeadersProperty = DependencyProperty.Register("LongDayHeaders", typeof(bool), typeof(Class1),
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.Inherits));
}
}
public class Class2: DependecyObject{
public static readonly DependencyProperty LongDayHeadersProperty;
public bool LongDayHeaders{
get{ return(bool)GetValue(LongDayHeadersProperty); }
set{ SetValue(LongDayHeadersProperty, value); }
}
static Class2(){
LongDayHeadersProperty = Class1.LongDayHeadersProperty.AddOwner(typeof(Class2));
}
}
But if I assign a DependencyPropertyDescriptor to both properties, it only fires for the Class1 and Class2 doesn't change.
Have I missed something in my understanding?
UPDATE
After some testing, I'm not even sure if my child control is considered a child control within the logical or visual tree. I think it is, but the lack of success leads me to believe otherwise.
There a many class2's which exist in an observable collection of class1. This, to me, makes them childs of class1? But even if I use RegisterAttach on class2, and set the property in class1, it doesn't seem to have any effect?
As MSDN states, the Inherits flag only works when you use RegisterAttached to create the property. You can still use the property syntax for the property.
Update
For clarity, here is how I would define the properties:
public class Class1 : FrameworkElement
{
public static readonly DependencyProperty LongDayHeadersProperty =
DependencyProperty.RegisterAttached("LongDayHeaders",
typeof(bool),
typeof(Class1),
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.Inherits));
public bool LongDayHeaders
{
get { return (bool)GetValue(LongDayHeadersProperty); }
set { SetValue(LongDayHeadersProperty, value); }
}
}
public class Class2: FrameworkElement
{
public static readonly DependencyProperty LongDayHeadersProperty =
Class1.LongDayHeadersProperty.AddOwner(typeof(Class2));
public bool LongDayHeaders
{
get{ return(bool)GetValue(LongDayHeadersProperty); }
set{ SetValue(LongDayHeadersProperty, value); }
}
}
If you want your children to be logical children of your control, you need to call the AddLogicalChild. Also, you should expose them through the LogicalChildren property. I must also point out that both classes must derive from FrameworkElement or FrameworkContentElement, as the logical tree is only defined for these elements.
Since you are using an ObservableCollection, you would handle the collection changed events and Add/Remove the children depending on the change. Also, the LogicalChildren property can just return your collection's enumerator.
You are confusing DependencyProperties with Attached (Dependency) Properties.
A DP is for when a class wants bindable, stylable etc properties on itself. Just like .NET properties, they are scoped within their classes. You can register for a property changed event on individual objects, but not globally. TextBox.Text is an example of this. Note that Label.Text is unrelated to TextBox.Text.
An AP is for when a class wants to decorate another object with additional properties. The class that declares the AP is able to listen for property changed events on ALL instances of other objects that have this AP set. Canvas.Left is an example of this. Note that you always have to qualify this setter: <Label Text="Hi" Canvas.Left="50"/>

Resources