I have a custom UserControl and I want to give it a custom property "MyProperty" which I can set in XAML. So that my XAML will look like this:
<EventDet:EventAddressControl
MyCustomProperty="formattype"
x:Name="EventSessionLocationControl"/>
How do I give the UserControl a custom attribute / property which I can then set in XAML?
If you are using CLRProperty you cannot use for Binding purpose.
public partial class MyCustomControl : UserControl
{
public MyCustomControl()
{
InitializeComponent();
}
public string MyCLRProperty { get; set; }
public string MyProperty
{
get { return (string)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(string), typeof(MyCustomControl ));
}
<my:MyCustomControl MyProperty="{Binding BindingProperty}"
MyCLRProperty="MyCLRProperty"/>
Just put a normal DependencyProperty in your class.
If you just want to set the value from xaml then you can use a regular property. If you want to use the property with triggers, styles, etc then you would need to use a dependency property to take advantage of those WPF features
Related
I have this user control codebehind:
public partial class MyControl : UserControl, INotifyPropertyChanged
{
public string MyProperty
{
get => (string)GetValue(MyPropertyProperty);
set
{
SetValue(MyPropertyProperty, value);
PropertyChanged?.Invoke(null, null);
}
}
public event PropertyChangedEventHandler PropertyChanged;
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register(nameof(MyProperty), typeof(string), typeof(string));
public MyControl()
{
InitializeComponent();
}
}
XAML:
<Grid>
<TextBox Text="{Binding MyProperty,
Mode=OneWayToSource,
UpdateSourceTrigger=PropertyChanged,
RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}"
VerticalAlignment="Center"
Height="20"
Margin="5"/>
</Grid>
I have one of those controls in my MainWindow and when I put a breakpoint on the "SetValue" line and change the value of the TextBox the breakpoint is hit and everything is right with the world. If I change the DP registering to:
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register(nameof(MyProperty), typeof(string), typeof(MyControl));
That breakpoint is no longer hit even though nothing else has been changed and afaik this is the better, correct way to register the DP.
Why is this happening and how to fix it?
Why is this happening and how to fix it?
Because you are violating WPF conventions.
And the XAML constructor (compiler) cannot guess how you are breaking this convention.
In fact, in the first option, you create a regular CLR property.
And, if you try to set a binding for it, you will most likely get a compilation error.
By convention, by which the XAML compiler works, the CLR wrapper should not contain ANY LOGIC, except for calling GetValue () and SetValue () and the owner of the property should be the class in which they are declared.
In this case, the compiler can find the original DependecyProperty and when setting / getting values it will use it without calling the CLR wrapper of the property.
And the CLR wrapper is designed for the convenience of the programmer when "hand-coding" in Sharp.
The INotifyPropertyChanged interface also looks completely pointless.
DependecyProperty has its own change notification mechanism and you don't need to duplicate it by calling PropertyChanged.
If you need to track changes in the DependecyProperty value, you must do this in the callback method specified when declaring DependecyProperty.
public partial class MyControl : UserControl
{
public string MyProperty
{
get => (string)GetValue(MyPropertyProperty);
set => SetValue(MyPropertyProperty, value);
}
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register(nameof(MyProperty), typeof(string), typeof(MyControl), new PropertyMetadata(string.Empty, MyPropertyChanged));
private static void MyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Here the logic for handling value change
MyControl myControl = (MyControl)d;
// Some Code
}
public MyControl()
{
InitializeComponent();
}
}
How would I achieve the same with the MyPropertyChanged callback?
This is very wrong, but if you need to, you can raise PropertyChanged in the callback method as well.
public partial class MyControl : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string MyProperty
{
get => (string)GetValue(MyPropertyProperty);
set => SetValue(MyPropertyProperty, value);
}
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register(nameof(MyProperty), typeof(string), typeof(MyControl), new PropertyMetadata(string.Empty, MyPropertyChanged));
private static readonly PropertyChangedEventArgs MyPropertyPropertyChangedArgs = new PropertyChangedEventArgs(nameof(MyProperty));
private static void MyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Here the logic for handling value change
MyControl myControl = (MyControl)d;
myControl.PropertyChanged?.Invoke(myControl, MyPropertyPropertyChangedArgs);
// Some Code
}
public MyControl()
{
InitializeComponent();
}
}
What would be the proper way to notify the ViewModel of the window the user control is in that the value of "MyProperty" has changed when the user types text in the TextBox?
If the VM needs to get the changed value of the MyProperty property, you need to set a binding to this property at the place where your control is used.
And already in the VM itself, process the change in its properties.
Usually, there is a corresponding method in a base class implementation for a VM.
But if there is no such method, then you can use the setter of this property.
Example:
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register(
nameof(MyProperty),
typeof(string),
typeof(MyControl),
new FrameworkPropertyMetadata(string.Empty, MyPropertyChanged) { BindsTwoWayByDefault = true });
<local:MyControl MyProperty="{Binding VmProperty}" .../>
public class MainViewModel: ....
{
public string VmProperty
{
get => _vmProperty;
set
{
if(Set(ref _vmProperty, value))
{
// Some code to handle value change.
}
}
}
}
Additional advice.
Your given implementation is not very suitable for UserControl.
You'd better change it to Custom Control.
I have in my C# WPF solution as follows:
Mainwindow with a startupControl (always running)
Dialogwindow with diffent other controls.
A public Helper-class containing some public static properties to indicate what department at customer is active, and for who i have focus on at the moment.
I want simply two XAML textBlocks displayed in my Startupcontrol to show the property names if and when the value for a department or costumer has been set.
I think it could properbly work smooth with some sort of binding, but i dont know anything about bindings, other than they exists.
Is it possible in any way from my controls in my dialogwindow, to change the value of the 2 textblocks in the Startupcontrol ?
As the program is small and I know exactly when the values change, I think i could make a function setting the value ex.:
activeDepartmentTextBlock.Text = HelperClass.ActiveDepartment.Name;
But from my control.cs in the DialogWindow, it seems to be possible to reach the activeDepartmentTextBlock.
Anyone who can help me ?
Since WPF 4.5, binding to static properties with property change notification is quite simple.
The example below assumes that you want to notify about the change of the ActiveDepartment property of the HelperClass (and not about the Name property of the Department object). In addition to the static property, declare a static event named StaticPropertyChanged and fire it when the static property changes:
public class Department
{
public string Name { get; set; }
}
public class HelperClass
{
public static event PropertyChangedEventHandler StaticPropertyChanged;
private static Department activeDepartment;
public static Department ActiveDepartment
{
get => activeDepartment;
set
{
activeDepartment = value;
StaticPropertyChanged?.Invoke(null,
new PropertyChangedEventArgs(nameof(ActiveDepartment)));
}
}
}
You can bind to a static property like this:
<TextBlock Text="{Binding Path=(local:HelperClass.ActiveDepartment).Name}"/>
Binding is a good solution but you have static property so you can't use binding infrastructure directly to get notified of updates since there's no DependencyObject (or object instance that implement INotifyPropertyChanged) involved.
If the value does change and you need to update TextBlock's value in main window yo can create a singleton instead of static class to contain the value and bind to that.
An example of the singleton:
public class HelperClass : DependencyObject {
public static readonly DependencyProperty ActiveDepartmentProperty =
DependencyProperty.Register( "ActiveDepartment", typeof( Department ),
typeof( HelperClass ), new UIPropertyMetadata( "" ) );
public Department ActiveDepartment {
get { return (Department) GetValue( ActiveDepartmentProperty ); }
set { SetValue( ActiveDepartmentProperty, value ); }
}
public static HelperClass Instance { get; private set; }
static HelperClass() {
Instance = new HelperClass();
}
}
So binding will work like in an example below:
<TextBox Text="{Binding Source={x:Static local:HelperClass.Instance}, Path=ActiveDepartment.Name}"/>
It might look like a hard way and that’s it. You can use events model instead and add the event to your HelperClass. MainWindow can add event handler and change activeDepartmentTextBlock value when event raised.
public MainWindow()
{
InitializeComponent();
HelperClass.Instance.DepartmentChanged += OnDepartmentChanged;
}
private void OnDepartmentChanged(Department newDepartment)
{
activeDepartmentTextBlock.Text = newDepartment.Name;
}
Update. If you want to have the simplest solution you can break encapsulation principle and pass MainWindow as a parameter to DialogWindow and make activeDepartmentTextBlock public. So you will be able to save the link to the MainWindow in the DialogWindow's field and just change the text when you need in DialogWindow:
this.mainWindow.activeDepartmentTextBlock.Text = HelperClass.ActiveDepartment.Name;
I attachted a Property to a WPF UserControl like this:
public partial class Datensatzliste : UserControl
{
public int Modulnummer
{
get { return (int)this.GetValue(ModulnummerProperty); }
set { this.SetValue(ModulnummerProperty, value); }
}
public static DependencyProperty ModulnummerProperty = DependencyProperty.Register("Modulnummer", typeof(int), typeof(Datensatzliste), new PropertyMetadata(0));
....
}
The Control is hosted in a WinForms application. When I select the Control in Visual Studio, the property is not shown. Ho do I geht the Property to show up in the property pane?
You could set the value of the property from the code-behind.
I'm creating a UserControl with a DependencyProperty but the DependencyProperty isn't getting the value that the caller is passing in.
I've discovered the following during my own investigation
If I use a built-in user control, such as TextBlock, everything works. This narrows the problem down to my UserControl's implementation (as opposed to the code that calls the UserControl)
My property changed callback that I register isn't even being called (well... at least the breakpoint isn't being hit)
If only see this problem when I use a binding to provide the dependency property, so this doesn't work:
<common:MyUserControl MyDP="{Binding MyValue}"/>
but I have no problems if I get rid of the binding and hardcode the value, so this works:
<common:MyUserControl MyDP="hardCodedValue"/>
Here's my UserControl's code behind:
public partial class MyUserControl : UserControl
{
public string MyDP
{
get { return (string)GetValue(MyDPProperty); }
set { SetValue(MyDPProperty, value); }
}
public static readonly DependencyProperty MyDPProperty =
DependencyProperty.Register(
"MyDP",
typeof(string),
typeof(MyUserControl),
new FrameworkPropertyMetadata(
"this is the default value",
new PropertyChangedCallback(MyUserControl.MyDPPropertyChanged)));
public static void MyDPPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
((MyUserControl)obj).MyDP = (string)e.NewValue;
}
public MyUserControl()
{
InitializeComponent();
this.DataContext = this;
}
}
And here's the xaml
<Grid>
<TextBlock Text="{Binding MyDP}"/>
</Grid>
Since I'm able to use built-in user controls such as TextBlock, I don't think that the error lies in my host code, but here it is, just so that you have a complete picture:
<StackPanel>
<common:MyUserControl MyDP="{Binding MyValue}"/>
</StackPanel>
public class MainWindowViewModel
{
public string MyValue { get { return "this is the real value."; } }
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel();
}
}
This line in the UserControl is wrong:
this.DataContext = this;
This makes the UserControl its own DataContext, so the binding is looking for a property called MyValue on the UserControl, and that property does not exist. You want the DataContext to be your view-model. If you don't set it explicitly, it will inherit the DataContext from its container (the Window in this case).
Delete that line, and you'll be closer. You also don't need that callback; remove that too.
You can update your control's view code like that:
<Grid>
<TextBlock x:Name="_textBlock"/>
</Grid>
And set a _textBlock's text property in MyDPPropertyChanged method:
public static void MyDPPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var control = ((MyUserControl)obj);
control.MyDP = (string)e.NewValue;
control._textBlock.Text = control.MyDP;
}
That will do the trick.
Kindly Implement INotifyPropertyChanged, and PropertyChangedEventHandler in side the control and also the viewmodel. secondly use SetCurrentValue method to set the value inside the control class rather setting it directly
If I have the following control:
public partial class MyControl : UserControl{
public string MyControlText{
get { return MyTextBox.Text; }
set { MyTextBox.Text = value; }
}
public MyControl(){ ... }
}
How can I bind to the "MyControlText" property when I place the control on one of my pages, like so:
<local:MyControl MyControlText={Binding Path=SomeField} />
Thanks!
You need to make the property a dependency property. The documentation for the DependencyProperty class shows you how to do this:
http://msdn.microsoft.com/en-us/library/system.windows.dependencyproperty.aspx#