CustomControl Property unable to set via dependency property - c#

I have created custom control which contains combo box to display colors and one dependency property SelectedIndex. I have used this control in mainwimdow.xaml page and tried to set selected index value via dependency property declared in mainwimdow.xaml.cs page.
But when I set any value via dependency property it doesn't work and if I set custom controls property directly in mainwimdow.xaml page it works fine. So this means custom control is working fine only value is not setting via dependency property.
Here is the code.
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public static readonly DependencyProperty ConditionsSelectedIndexProperty =
DependencyProperty.Register(
"ConditionsSelectedIndex",
typeof(int),
typeof(MainWindow),
new UIPropertyMetadata(3));
public int ConditionsSelectedIndex
{
get { return (int)GetValue(ConditionsSelectedIndexProperty); }
set { SetValue(ConditionsSelectedIndexProperty, value); }
}
public MainWindow()
{
InitializeComponent();
SetCurrentValue(ConditionsSelectedIndexProperty, 5);
}
}
MainWindow.xaml:
<controls:ColorComboBoxControl x:Name="cmbConditions"
SelectedIndex="{Binding ConditionsSelectedIndex}"
Grid.Row="0" Grid.Column="0" />
Following code works fine if I assign hardcoded value like
<controls:ColorComboBoxControl x:Name="cmbConditions"
SelectedIndex="5" Grid.Row="0" Grid.Column="0" />
If I tried this to check dependency property is working or not then it's work
<TextBlock Text="{Binding Path=ConditionsSelectedIndex}"
Grid.Column="1" Height="25" Margin="42,143,77,143" >
</TextBlock>
So both custom control and dependency property are working fine if tested independently. But its not working when tried to set custom control property via another dependency property.

Related

How to create a Dependency Property for Binding

I'm working on a "simple" case. I like to create a new custom control which implements a DependencyProperty. In the next step I like to create a binding for updating the properties in both directions. I've builded a simple sample for this case, but the binding doesn't seem to work. I've found a way for updating the DPControl's property by using the FrameworkPropertyMetadata, but I don't know whether it's also a good idea to use the OnPropertyChanged event.
HERE is my sample project:
My control contains simply a Label
<UserControl x:Class="WPF_MVVM_ListBoxMultiSelection.DPControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WPF_MVVM_ListBoxMultiSelection"
mc:Ignorable="d" Height="84.062" Width="159.641">
<Grid Margin="0,0,229,268">
<Label Content="TEST" x:Name="label" Margin="0,0,-221,-102"/>
</Grid>
</UserControl>
and implement a custom dependency property. Currently, I have also implemented the PropertyChanged method for the FramePropertyMetadata and set in this method the label's content, but I like to get it work in both directions.
public partial class DPControl : UserControl
{
public DPControl()
{
InitializeComponent();
}
public string MyCustomLabelContent
{
get { return (string)GetValue(MyCustomLabelContentProperty);}
set
{
SetValue(MyCustomLabelContentProperty, value);
}
}
private static void OnMyCustomLabelContentPropertyChanged(DependencyObject source,
DependencyPropertyChangedEventArgs e)
{
DPControl control = (DPControl)source;
control.label.Content = e.NewValue;
}
public static readonly DependencyProperty MyCustomLabelContentProperty = DependencyProperty.Register(
"MyCustomLabelContent",
typeof(string),
typeof(DPControl),
new FrameworkPropertyMetadata(null,
OnMyCustomLabelContentPropertyChanged
)
);
I use this control simply in a Window by:
<local:DPControl MyCustomLabelContent="{Binding MyLabelContent, Mode=TwoWay}" Margin="72,201,286,34"/>
MyLabelContent is a property in the ViewModel, which has implemented also the INotifyPropertyChanged interface.
public class ViewModel_MainWindow:NotifyPropertyChanged
{
private string _myLabelContent;
public string MyLabelContent
{
get { return _myLabelContent; }
set { _myLabelContent = value;
RaisePropertyChanged();
}
}...
So how can I get it work: Using the binding feature with my new control on custom properties.
In your UserControl:
<Label
Content="{Binding MyCustomLabelContent, RelativeSource={RelativeSource AncestorType=UserControl}}"
x:Name="label" Margin="0,0,-221,-102"/>
And get rid of that property-changed callback. All you need is the Binding.
I like to get it work in both directions
To make the dependency property two-way by default:
public static readonly DependencyProperty MyCustomLabelContentProperty =
DependencyProperty.Register(
"MyCustomLabelContent",
typeof(string),
typeof(DPControl),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)
);
I omitted the unnecessary property change handler.
It can't usefully be two-way now, because Label.Content can't generate its own value. If you want your UserControl to set the value in its codebehind, that's easy:
MyCustomLabelContent = "Some arbitrary value";
If you did the binding like I showed you, that will update the Label in the UserControl XAML as well as the viewmodel property bound to the UserControl's dependency property.
If you want the XAML to set it, you'll need to
Lastly, this:
Margin="0,0,-221,-102"
Is not a good way to do layout. WPF layout with Grid, StackPanel, etc. is much easier and more robust.

XAML binding not working on dependency property?

I am trying (and failing) to do data binding on a dependency property in xaml. It works just fine when I use code behind, but not in xaml.
The user control is simply a TextBlock that bind to the dependency property:
<UserControl x:Class="WpfTest.MyControl" [...]>
<TextBlock Text="{Binding Test}" />
</UserControl>
And the dependency property is a simple string:
public static readonly DependencyProperty TestProperty
= DependencyProperty.Register("Test", typeof(string), typeof(MyControl), new PropertyMetadata("DEFAULT"));
public string Test
{
get { return (string)GetValue(TestProperty); }
set { SetValue(TestProperty, value); }
}
I have a regular property with the usual implementation of INotifyPropertyChanged in the main window.
private string _myText = "default";
public string MyText
{
get { return _myText; }
set { _myText = value; NotifyPropertyChanged(); }
}
So far so good. If I bind this property to a TextBlock on the main window everything works just fine. The text update properly if the MyText changes and all is well in the world.
<TextBlock Text="{Binding MyText}" />
However, if I do the same thing on my user control, nothing happens.
<local:MyControl x:Name="TheControl" Test="{Binding MyText}" />
And now the fun part is that if I do the very same binding in code behind it works!
TheControl.SetBinding(MyControl.TestProperty, new Binding
{
Source = DataContext,
Path = new PropertyPath("MyText"),
Mode = BindingMode.TwoWay
});
Why is it not working in xaml?
The dependency property declaration must look like this:
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register(
nameof(Test),
typeof(string),
typeof(MyControl),
new PropertyMetadata("DEFAULT"));
public string Test
{
get { return (string)GetValue(TestProperty); }
set { SetValue(TestProperty, value); }
}
The binding in the UserControl's XAML must set the control instance as the source object, e.g. by setting the Bindings's RelativeSource property:
<UserControl x:Class="WpfTest.MyControl" ...>
<TextBlock Text="{Binding Test,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</UserControl>
Also very important, never set the DataContext of a UserControl in its constructor. I'm sure there is something like
DataContext = this;
Remove it, as it effectively prevents inheriting a DataContext from the UserConrol's parent.
By setting Source = DataContext in the Binding in code behind you are explicitly setting a binding source, while in
<local:MyControl Test="{Binding MyText}" />
the binding source implicitly is the current DataContext. However, that DataContext has been set by the assignment in the UserControl's constructor to the UserControl itself, and is not the inherited DataContext (i.e. the view model instance) from the window.

UserControl DataContext Binding

I have three projects in my solution:
My main WPF Application which contains a MainWindow + MainViewModel
UserControl Library with a UserControl (ConfigEditorView)
UIProcess class with the ViewModel for the UserControl (ConfigEditorViewModel)
In my MainWindow I want to use the UserControl with the ViewModel of UIProcess.
First I set the UserControl in my MainWindow:
<TabItem Header="Editor">
<Grid>
<cel:ConfigEditorView DataContext="{Binding ConfEditModel, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</TabItem>
I don't know which of these properties I need here, so I put all together but it still doesn't work.
Then I've set this in my MainViewModel:
public ConfigEditorViewModel ConfEditModel { get; set; }
With simple method that is bound to a Button:
private void doSomething()
{
ConfEditModel = new ConfigEditorViewModel("Hello World");
}
My ConfigEditorViewModel looks basically like this:
public class ConfigEditorViewModel : ViewModelBase
{
private string _Description;
public string Description
{
get
{
return _Description;
}
set
{
_Description = value;
base.RaisePropertyChanged();
}
}
public ConfigEditorViewModel(string t)
{
Description = t;
}
}
The description is bound to a TextBox in my UserControl.
<TextBox Grid.Row="1" Grid.Column="1" Margin="0,0,0,10" Text="{Binding Description}"/>
When I start the application and click the Button the TextBox should contain "Hello World" but it's empty.
What I've done wrong?
i gave you a general answer:
within a "real(a usercontrol you wanna use with different viewmodels with different property names)" usercontrol you bind just to your own DependencyProperties and you do that with ElementName or RelativeSource binding and you should never set the DataContext within a UserControl.
<UserControl x:Name="myRealUC" x:class="MyUserControl">
<TextBox Text="{Binding ElementName=myRealUC, Path=MyOwnDPIDeclaredInMyUc, Path=TwoWay}"/>
<UserControl>
if you do that you can easily use this Usercontrol in any view like:
<myControls:MyUserControl MyOwnDPIDeclaredInMyUc="{Binding MyPropertyInMyViewmodel}"/>
and for completeness: the Dependency Property
public readonly static DependencyProperty MyOwnDPIDeclaredInMyUcProperty = DependencyProperty.Register(
"MyOwnDPIDeclaredInMyUc", typeof(string), typeof(MyUserControl), new PropertyMetadata(""));
public bool MyOwnDPIDeclaredInMyUc
{
get { return (string)GetValue(MyOwnDPIDeclaredInMyUcProperty); }
set { SetValue(MyOwnDPIDeclaredInMyUcProperty, value); }
}
Your view models (and, optionally, models) need to implement INotifyPropertyChanged.
Binding's aren't magic. There is no inbuilt mechanism that allows for code to be notified when a plain old property's value changes. You'd have to poll it in order to check to see if a change happened, which would be very bad, performance-wise.
So bindings will look at the objects they are bound against and see if they implement INotifyPropertyChanged and, if so, will subscribe to the PropertyChanged event. That way, when you change a property and fire the event, the binding is notified and updates the UI.
Be warned, you must implement the interface and use it correctly. This example says it's for 2010, but it works fine.

Property Change not showing in Visual Studio designer

Using VS2015 I'm adding some custom functionality to a TextBlock for a small app and, since I can't derive from TextBlock itself (it's sealed), I'm deriving from UserControl.
In my xaml file, I have
<TextBlock x:Name="innerText"/>
As the only element within the usercontrol.
In my code-behind, I have the following used for accessing the text:
public string Label
{
get { return innerText.Text; }
set {
if (value != innerText.Text)
{
innerText.Text = value;
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs("Label"));
}
}
}
This works great when I'm running my app. On other pages, I am able to add instances of the control and set the "Label" property correctly. Unfortunately, the value of the "Label" property doesn't carry through to the inner textbox within the designer itself.
How can I get the value to update in the designer? While not strictly necessary (as I said, at run-time it works fine), it would make layout in the designer much easier for me.
Update:
I also tried using a DependencyProperty, with the same issue. Run-time works great, design-time shows nothing.
public string Label
{
get { return GetValue(LabelProperty).ToString(); ; }
set { SetValue(LabelProperty, value); }
}
public static readonly DependencyProperty LabelProperty = DependencyProperty.Register("Label", typeof(string), typeof(AutoSizingText), new PropertyMetadata(string.Empty));
And then, in the xaml, I set the DataContext for the entire control:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
And tried to bind the Text value:
<TextBlock Text="{Binding Label}" />
I would recommend using a dependency property instead of relying on setting the innerText element's Text property. A dependency property will behave just like any other property on a control, including updating in design mode.
public string Label
{
get { return (string)GetValue(LabelProperty); }
set { SetValue(LabelProperty, value); }
}
// Using a DependencyProperty as the backing store for Label. This enables animation, styling, binding, etc...
public static readonly DependencyProperty LabelProperty =
DependencyProperty.Register("Label", typeof(string), typeof(MyClassName), new PropertyMetadata(string.Empty));
And your XAML will look like this:
<UserControl x:Name="usr" ...>
...
<TextBlock Text="{Binding Label, ElementName=usr}" ... />
...
</UserControl>
Pro tip: Type propdp, then Tab, Tab to quickly create a dependency property.
Here's an example usage:
<local:MyUserControl Label="Le toucan has arrived"/>
Note: You do not need to set the DataContext to Self when using a dependency property, this will generally screw things up as the UserControl should not set it's own DataContext, the parent control should.

wpf target property dependency

I'm trying to create a simple Bindable property called MyBoolValue in my UserControl class
First, here the xaml
<UserControl x:Class="TMDE.Controls.SimNaoRadioPicker"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="16"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<CheckBox Grid.Column="0" Content="Teste" IsChecked="{Binding Path=MyBoolValue}" x:Name="chk" />
</Grid>
</UserControl>
And here the code-behind:
public partial class SimNaoRadioPicker : UserControl
{
public SimNaoRadioPicker()
{
InitializeComponent();
}
public bool? MyBoolValue
{
get
{
return (bool?)GetValue(MyCustomPropertyProperty);
}
set
{
SetValue(MyCustomPropertyProperty, value);
}
}
// Using a DependencyProperty as the backing store for MyCustomProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyCustomPropertyProperty =
DependencyProperty.Register("MyBoolValue",
typeof(bool?), typeof(SimNaoRadioPicker),
new UIPropertyMetadata(MyPropertyChangedHandler));
public static void MyPropertyChangedHandler(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
// Get instance of current control from sender
// and property value from e.NewValue
// Set public property on TaregtCatalogControl, e.g.
((SimNaoRadioPicker)sender).chk.IsChecked = (bool?)e.NewValue;
}
}
Now, when a try to use this control in another Window, like this:
<my:SimNaoRadioPicker x:Name="test" MyBoolValue="{Binding QCV_Localizacao_Reutilizacao}" Height="16" HorizontalAlignment="Left" Margin="287,456,0,0" VerticalAlignment="Top" Width="167" />
the Binding doesnt working, the property QCV_Localizacao_Reutilizacao doesnt get update and vice-versa.
The DataContext of the Window its a class that implements INotifyPropertyChanged, so the
property "QCV_Localizacao_Reutilizacao" should work ok.
Also if I use a regular CheckBox instead of my UserControl, its works okay
What I'm doing wrong?
I would remove the nullable part of the boolean and just make it a boolean, then set binding modes to two way.
There are two major issues -
First, your binding mode needs to be TwoWay which you can achieve in two ways -
Either specifed it to be TwoWay in xaml like this -
<my:SimNaoRadioPicker MyBoolValue="{Binding QCV_Localizacao_Reutilizacao,
Mode=TwoWay}"/>
The drawback with above apporach is that you have to explicitly set the mode whenever you are using the UserControl's instance.
Another approach would be to modify your DP itself to say that it always be bind by default in a TwoWay mode like this using FrameworkPropertyMetadata -
public static readonly DependencyProperty MyCustomPropertyProperty =
DependencyProperty.Register("MyBoolValue",
typeof(bool?), typeof(SimNaoRadioPicker),
new FrameworkPropertyMetadata(false,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
MyPropertyChangedHandler));
Secondly, QCV_Localizacao_Reutilizacao property lies in your Window's DataContext. But, by default any control will look for binding in its own dataContext so you explicilty need to tell it to look into Window's DataContext using RelativeSource like this -
<my:SimNaoRadioPicker MyBoolValue="{Binding QCV_Localizacao_Reutilizacao,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=Window}/>

Categories