Reputation: 3185
I'm having problems when trying to implement a user control that contains a view model and some dependency properties.
Right now, the idea is to have a text box on the UC that contains a watermark property which should allow for the developer using it to pass a localized string from a resource file to it via xaml.
Given that the UC will have to process some info, I need to create a view model for it.
What I have so far is as follow:
On the user control I have one control which contains a string property named "Watermark", the value for it is binded to my VM watermark property:
<Grid x:Name="LayoutRoot" Background="Gray">
<controls:CustomTextBox Watermark="{Binding Path=WatermarkTextValue}"/>
</Grid>
And the view model looks like this:
private string watermarkText;
public string WatermarkTextValue
{
get
{
return watermarkText;
}
set
{
watermarkText = value;
this.OnPropertyChanged(() => this.WatermarkTextValue);
}
}
The User control code behind contains the dependency property to use in order to bind the watermark of the view model against a resource file entry and in the constructor create a binding between the VM property and the dependency one:
public partial class SearchFilterUserControl : UserControl
{
public SearchFilterUserControl()
{
InitializeComponent();
this.DataContext = new SearchFilterViewModel();
var viewModelPropertyBinding = new Binding("WatermarkTextValue") { Mode = BindingMode.TwoWay, Source = this.DataContext };
this.SetBinding(WatermarkTextProperty, viewModelPropertyBinding);
}
public string WatermarkText
{
get
{
return (string)this.GetValue(WatermarkTextProperty);
}
set
{
this.SetValue(WatermarkTextProperty, value);
}
}
public static readonly DependencyProperty WatermarkTextProperty =
DependencyProperty.Register("WatermarkText", typeof(string), typeof(SearchFilterUserControl), new PropertyMetadata(string.Empty));
}
The main issue here is that, when using the UC from a view; I can only see values that are hardcoded in xaml, any other kind of binding won't work, so out of these two lines:
<userControls:SearchFilterUserControl WatermarkText="{Binding Path=SearchFilterUserControl_SearchWatermarkText, Source={StaticResource ResourceManagementClientResources}}"/>
<userControls:SearchFilterUserControl WatermarkText="Hardcoded text"/>
I see an empty text box and another one with the "Hardcoded text" watermark in it!
Upvotes: 1
Views: 537
Reputation: 102763
There are a few problems here, but let's start with this:
public SearchFilterUserControl()
{
InitializeComponent();
this.DataContext = new SearchFilterViewModel();
}
When you do this, you are changing the data context of the control, which will break any bindings for the user of the control. That is, with this:
<controls:SearchFilterUserControl Watermark="{Binding Path=WatermarkTextValue}" />
the runtime will now look for "WatermarkTextValue" in the "SearchFilterViewModel" that is its new data context.
One way of getting around this issue is to apply the data context to a child element of your control (typically "LayoutRoot" or similar). That way the outer DataContext will be preserved. Note that you have to do this in the "OnApplyTemplate" override -- you can't do it in the constructor, as the template elements won't be loaded yet. Something like this:
public SearchFilterUserControl()
{
InitializeComponent();
this.DataContext = new SearchFilterViewModel();
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
GetTemplateChild("LayoutRoot").DataContext = new SearchFilterViewModel();
}
A second problem is that it looks like you're exposing "WatermarkTextValueProperty" as a binding target to the consumer of your control, then attempting to re-set it as a target of your internal binding to your view model. This obviously will not work.
My suggestion is to simply ditch the view model, and use a combination of an IValueConverter
in your template binding, and/or the dependency property's "changed" event, to handle whatever processing you need. That will be a lot less convoluted.
If you insist on using the internal view model, then you will need to figure out a way to separate the "external" binding (which the consumer of your control sets) from the "internal" binding which your control uses to display the text.
Upvotes: 1