Dependency property in user control doesn't have any effect - c#

In my user control (class LabeledBox), I've added dependency property as follows.
public static readonly DependencyProperty HorizontalProperty
= DependencyProperty.Register(
"Horizontal",
typeof (Orientation),
typeof (LabeledBox),
new PropertyMetadata(default(Orientation)));
public Orientation Horizontal
{
get { return (Orientation) GetValue(HorizontalProperty); }
set { SetValue(HorizontalProperty, value); }
}
However, when setting it according to the below, doesn't give me any difference in behavior. In fact, the setter in the property doesn't get called. What do I miss?
<local:LabeledBox x:Name="Info field"
Description="Info"
Horizontal="Horizontal" />
The component in question has a stack panel as outermost control and it's bound like this.
<StackPanel Name="TheContainer" Orientation="{Binding Horizontal}">
Perhaps I've done the binding incorrectly?

Give a name to your UserControl:
<UserControl .... x:Name="labeledBox">
And use the binding like this:
<StackPanel Name="TheContainer" Orientation="{Binding Horizontal, ElementName=labeledBox}">

Yes your Binding is not well try to update it to be seems like:
<StackPanel Name="TheContainer"
Orientation="{
Binding Horizontal,
RelativeSource={RelativeSource AncestorType=local:LabeledBox}}"/>

Related

How to write getters and setters to convert WPF dependency property? [duplicate]

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.

WPF Bind User Control property not working

I have a user control and im trying to bind one of its properties
User Control Xaml
<UserControl x:Class="pi_browser.Testing.Example"
...
x: Name="LabelControl">
<StackPanel x:Name="RootStackPanel">
<Label Content="{Binding Text, ElementName=LabelControl}"/>
</StackPanel>
</UserControl>
User Control Codebehind
public partial class Example : UserControl
{
public Example()
{
InitializeComponent();
ExampleViewModel vm = new ExampleViewModel(State);
DataContext = vm;
}
public Boolean State
{
get { return (Boolean)this.GetValue(StateProperty); }
set { this.SetValue(StateProperty, value); }
}
public static readonly DependencyProperty StateProperty =
DependencyProperty.Register("State",
typeof(Boolean), typeof(Example), new PropertyMetadata(false));
}
Main Page View Model
class ExampleViewModel
{
public ExampleViewModel(bool v)
{
val = v;
}
bool val;
public string Text { get => val ? "This worked" : "This didnt work"; }
}
Main Window Xaml
<Window x:Class="pi_browser.Testing.Tester" ... >
<Grid>
<local:Example State="True"/>
</Grid>
</Window>
In this example I didn't bind the State variable, I only passed a literal, but ideally I would like to bind to actual values.
State is a boolean, yet you bind to Text. Let us fix one issue by creating a dependency property Text on your User Control. We shall fix the Text issue and not the boolean State issue. Once you fix that, do the same for State.
So to fix Text we need to fix why this fails:
<Label Content="{Binding Text, ElementName=LabelControl}"/>
You set the ElementName to be the UserControl itself, which is what one wants. But then you tell the binding to look for (remember binding is just reflection of an object under the covers) the property Text. The property Text does not exist on that instance/class...but State does. Its obvious to bind to a newly created Text dependency property on the user control to fix the first issue.
Then when you instantiate the control on your main page, you need to then, and only then bind to Text because that property also resides on your viewmodel.
So three things, along with the change mentioned on the UserControl:
Make your ViewModel adhere to INotifyPropertyChanged and make the Text property use the notification mechanism you install.
Make sure that your main page has its DataContext set to a vailid instance of your ViewModel class.
Bind to Text such as <local:Example State="{Binding Text}"/>
Once that is done, the Text value will properly flow towards the UserControl.

How to bind multiple user controls?

I have created the following User Control:
<UserControl x:Class="TextBinder" ...>
<TextBox Text="{Binding ????}" />
</UserControl>
Now I am using my user control twice in my MainWindow. The MainWindow is then bound to my ViewModel (I set the DataContext). Now the problem is: how can I bind my user controls to the user_controlViewModel?
In my ViewModel, I have created two objects let's call them UC_1 and UC_2, they contain different texts and I would like to bind them to their respective user control in my MainWindow.
What should I put at ????
Note: please do not simplify mu TextBox to double textboxes in one usercontrol. This is not what I would like since in my real life example I have more stuff than textbox only and the usercontrol should be used multiple times in one view.
Thanks!
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); }
}
Thats right, you need yo declare a dependency property in your UserControl:
public partial class TextBinder:UserControl
{
public static readonly DependencyProperty textproperty =
DependencyProperty.Register("Text", typeof(string), typeof(TextBinder));
public string Text
{
get
{
return this.GetValue(textproperty) as string;
}
set
{
this.SetValue(textproperty,value);
}
}
}
And then, you can use your usercontrol in your window at this way:
<YourNamespace:TextBinder Text={Binding ViewModelProperty}/>

WPF Dependency property binding breaks and always uses the default value

I have a dependency property defined as below. It is defined in xaml.cs of Childusercontrol. It always uses the default value of RGB(255,0,0) ie. Red.
public Color ForeColor
{
get {return (Color)this.GetValue(ForeColorProperty); }
set {this.SetValue(ForeColorProperty, value);}
}
public static readonly DependencyProperty ForeColorProperty = DependencyProperty.Register("ForeColor", typeof(Color), typeof(Childusercontrol), new PropertyMetadata(Color.FromRgb(255,0,0), OnCurrentForeColorPropertyChanged));
private static void OnCurrentForeColorPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
Childusecontrol control = source as Childusecontrol;
Color fcolor= (Color)e.NewValue;
}
The value is passed through xaml from parent usercontrol as
<UC:Childusercontrol ForeColor="{Binding ChildForeColor}"/>
ChildForeColor is a property of type Color in ViewModel of ParentUserControl and is defined as below.
private Color _ChildForeColor;
public Color ChildForeColor
{
get
{
return _ChildForeColor ;
}
set
{
if (_ChildForeColor != value)
{
_ChildForeColor = value;
RaisePropertyChanged(()=> ChildForeColor );
}
}
}
And ChildForeColor property is set as below, in the parentusercontrol's constructor.
The value being passed as constructor parameter is blue.
public Parentusercontrol(System.Drawing.Color ForeColor)
{
ChildForeColor = Color.FromRgb(ForeColor.R, ForeColor.B, ForeColor.G);
}
But, the InitializeComponent(); of Parent control's xaml.cs clears the value of dependency property and hence, only the default value is used.
Do I have to change the definition of the dependency property? How to fix this bug?
This worked perfectly fine for me!
ChildControl
I gave the UserControl a Name in Xaml i.e
<UserControl ... (all normal namespaces)... x:Name="Child">
<Border>
<Border.Background>
<SolidColorBrush Color="{Binding ForeColor, ElementName=child}"/>
</Border.Background>
</Border>
</UserControl>
The property "ForeColor" is a dependency property as you defined it yourself. This control works perfectly on its own too.
ParentControl
I did the same as with ChildControl. i.e. gave it a name.
<UserControl ... (Usual NS)... x:Name="parent">
<Border BorderThickness="2">
<local:ChildUserControl Margin="5" ForeColor="{Binding ChildForeColor, ElementName=parent}"/>
<Border.BorderBrush>
<SolidColorBrush Color="{Binding ChildForeColor, ElementName=parent}"/>
</Border.BorderBrush>
</Border>
</UserControl>
This also works fine with testing the C# Class looks as follows
public ParentUserControl(System.Drawing.Color c)
{
InitializeComponent();
Color c2 = Color.FromRgb(c.R, c.G, c.B);
ChildForeColor = c2;
}
private Color _ChildForeColor = Color.FromRgb(0, 255, 0);
public Color ChildForeColor
{
get { return _ChildForeColor; }
set
{
if (value != _ChildForeColor)
{
_ChildForeColor = value;
OnPropertyChanged(() => ChildForeColor);
}
}
}
I have assigned the _ChildForeColor a value just for testing, but this is not needed. Please note however that if you run a NotifyPropertyChanged event this cannot happen before InitializeComponent(); This I guess is because nothing yet has been initialized to listen to the change. Therefore you have 2 options. Remove OnPropertyChanged and assign color before InitializeComponent, or use OnPropertyChanged but only assign color after InitializeComponent. The first solution will still work because the property value is changed before the components go and look for the value.
Window for using constructing the controls
This is a bit more tricky as you have assigned a constructor that takes a variable. So my code looks as follows:
public Control ParContent
{
get { return (ContentControl)GetValue(ParContentProperty); }
set { SetValue(ParContentProperty, value); }
}
//Register Dependency ParContent Property
public static readonly DependencyProperty ParContentProperty = DependencyProperty.Register("ParContent", typeof(ContentControl), typeof(MainWindow), new PropertyMetadata( ));
public MainWindow()
{
InitializeComponent();
ParContent = new ParentUserControl(System.Drawing.Color.Blue);
}
and in Xaml
<Window ...Title="MainWindow" Height="478.784" Width="736.87" x:Name="win">
<Grid>
<local:ChildUserControl HorizontalAlignment="Left" Height="100" Margin="122,298,0,0" VerticalAlignment="Top" Width="100"/>
<ContentControl x:Name="Parent" Content="{Binding ParContent,ElementName=win}" HorizontalAlignment="Left" Margin="106,49,0,0" VerticalAlignment="Top" Height="79" Width="93"/>
</Grid>
</Window>
As I said this worked perfectly fine by me and all the properties keep their values.
Possible solutions:
Make sure the parent's childForeColor has a color assigned to it especially when using ordinary properties.
If you use ordinary properties in Parent control make sure INotifyPropertyChange is called if the color is changed after Initialize (Which I guess you subscribe to already)
perhaps use FrameworkPropertyMetadata instead and then add flag AffectsRender - don't think this is the problem, but worth a shot
Play around with the Binding Mode - although I do not think this is the real issue either
If you are working with 2 x controls where 1 property is most likely going to inherit from another use Inherited properties rather - http://msdn.microsoft.com/en-us/library/ms753197(v=vs.110).aspx
Bottom line I have a suspicion that the Parent's "ChildForeColor" might be causing the problem as the above seems ok to me at first glance.
EDIT
Try doing the following. In xaml give your parent control a name x:name="Parent" then in the binding mode do this
<UC:Childusercontrol ForeColor="{Binding ChildForeColor, ElementName="Parent"}"/>
This should sort out any binding issues if the problem lies with the binding.
However you say "Parent control's xaml.cs clears the value of dependency property and hence, only the default value is used." Which indicates that the problem is not with binding or with the child control as far as I can gather...
I also assumed you have stepped through the code so after you hit this
ChildForeColor = Color.FromRgb(ForeColor.R, ForeColor.B, ForeColor.G);
ChildForeColor appears correct and then if you override OnInitialized() and evaluate the value of ChildForeColor after base.OnInitialized(e); has run the ForeColor is still unchanged?
With this I also assume you have not removed InitializeComponent(); from the constructor, and InitializeComponent(); comes after ChildForeColor = ....! In your constructor you do not show where InitializeComponent() is and I assumed it was just for easy reading purpose.
If ForeColor remained unchanged at this point and assuming base.OnInitialized is the first method that runs in OnInitialized. Then Initialization is not the problem, then the alternative suggestion is to change ChildForeColor to a proper dependency property:
public Color ChildForeColor
{
get { return (Color)GetValue(ChildForeColorProperty); }
set { SetValue(ChildForeColorProperty, value); }
}
//Register Dependency ChildForeColor Property
public static readonly DependencyProperty ChildForeColorProperty = DependencyProperty.Register("ChildForeColor", typeof(Color), typeof(ParentControl), new FrameworkPropertyMetadata());
and see if that changes it.

Issue with binding custom DependencyProperty to ListView.SelectedItem

I'm having an issue with a binding that I'm trying to implement. It will update the DP once, but after that, it's never updated again.
In XAML I have two controls binding to a listview.selected item.
<controls:MapControl DataContext="{Binding ElementName=availableMapsListView, Path=SelectedItem}" MapData="{Binding .}">
and
<TextBlock DataContext="{Binding ElementName=availableMapsListView, Path=SelectedItem}" Text="{Binding Name}" />
The textblock update as expected with each change of the listview's selected item.
My custom control creates the dependency property like so:
public class MapControl : UserControl
{
public MapData MapData
{
get { return (MapData)GetValue(MapDataProperty); }
set { SetValue(MapDataProperty, value); }
}
public static readonly DependencyProperty MapDataProperty =
DependencyProperty.Register("MapData", typeof(MapData), typeof(MapControl),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnMapDataPropertyChanged),
new CoerceValueCallback(OnMapCoerceValue)
)
);
private static void OnMapDataPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != null)
{
((MapControl)source).MapData = (MapData)e.NewValue;
}
}
private static object OnMapCoerceValue(DependencyObject dpo, Object obj)
{
return obj;
}
...
}
I'm pretty much at my wits end and not sure what I should do from here. Any help is greatly appreciated.
Not sure exactly what you're trying to achieve or why your code appears so convoluted. If you explain more someone may be able to provide you with a much simpler solution.
That said, by the sounds of it the problem is simply that you're overwriting the binding with a local value. This looks like the culprit:
((MapControl)source).MapData = (MapData)e.NewValue;
When you do this, the MapControl.MapData property will no longer be bound to '.' Instead, it will take on whatever value you've assigned. So your MapControl.DataContext property is likely perfectly correct, but it's not being transferred to the MapData property because you've destroyed the binding.
I had the same error last week. My solution was simple : When you explicitly define a DependencyProperty you must also explicitly define the mode to TwoWay.
<TextBlock DataContext="{Binding ElementName=availableMapsListView,
Path=SelectedItem}"
Text="{Binding Name, Mode=TwoWay}" />

Categories

Resources