DependencyProperty ... can set, but can't get value out via binding - wpf

I have a Window, containing a UserControl 'TemplateEditor'. The (shortened) TemplateEditor XAML is:
<UserControl x:Class="xxx.Windows.Core.Controls.TemplateEditor"
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"
mc:Ignorable="d"
x:Name="userControl">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox Grid.Row="1" x:Name="textBox" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" />
</Grid>
</UserControl>
I want to be able to bind data through the TemplateEditor into the TextBox "textBox". I'm using a DependencyProperty to mask the TextBox in the code-behind:
namespace xxx.Windows.Core.Controls
{
public partial class TemplateEditor : UserControl
{
public string Text
{
get
{
string s=(string)GetValue(TextProperty);
return s;
}
set
{
if (Text != value)
{
SetValue(TextProperty, value);
}
}
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(TemplateEditor),new PropertyMetadata(null,Text_PropertyChanged));
public TemplateEditor()
{
InitializeComponent();
}
private static void Text_PropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
((TemplateEditor)source).textBox.Text = (string)e.NewValue;
}
}
}
So from that you can see I have a DependencyProperty Text that I have a callback on to pick up changes made by to the binding (say, from the ViewModel) and apply to the TextBox value.
This works.
The problem is, I can't seem to have the binding work in reverse, ie. to get the value back out of the Text property (and therefore the TextBox) and back in to the binding consumer (the ViewModel). I've debugged calling GetValue(TextProperty) and this returns the correct value so the DP dictionary is correctly updating.
Given the following Binding in the parent Window's XAML:
<src:ApplicationWindowBase x:Class="xxx.Windows.Client.Filing"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:src="clr-namespace:xxx.Windows.Core;assembly=MIGTurbo1.Windows.Core"
xmlns:viewmodel="clr-namespace:xxx.Windows.Client.ViewModel"
xmlns:converters="clr-namespace:xxx.Windows.Client.Converters"
xmlns:xxx_Windows_Core_Controls="clr-namespace:xxx.Windows.Core.Controls;assembly=xxx.Windows.Core"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="File Item(s)" Height="450" Width="550" ShowInTaskbar="False">
<src:ApplicationWindowBase.Resources>
<viewmodel:ViewModelLocator x:Key="ViewModelLocator" d:IsDataSource="True"/>
<converters:FileableItemNameStringConverter x:Key="fileableItemNameStringConverter" />
<converters:FileableItemTypeStringConverter x:Key="fileableItemTypeStringConverter" />
<converters:FileableItemMetaDataStringConverter x:Key="fileableItemMetaDataStringConverter" />
<converters:FileableItemIconConverter x:Key="fileableItemIconConverter" />
</src:ApplicationWindowBase.Resources>
<src:ApplicationWindowBase.DataContext>
<Binding Mode="OneWay" Path="Filing" Source="{StaticResource ViewModelLocator}"/>
</src:ApplicationWindowBase.DataContext>
<Grid>
<!-- SNIP -->
<xxx_Windows_Core_Controls:TemplateEditor Grid.Column="1" Grid.Row="1" Margin="0" Text="{Binding Comment, Mode=TwoWay}" d:LayoutOverrides="Width, Height"/>
<!-- SNIP -->
</src:ApplicationWindowBase>
I am using MVVM Light and the ViewModel does bind correctly. There are other control/field bindings (ommitted) that work fine. THe problem is that the Text property binding on the Comment ViewModel property is not working. I can set it fine (ie. set value in ViewModel, then bind) but the user-entered value in Text never goes into the ViewModel Comments property.
What am I doing wrong?

Try this:
<TextBox Text="{Binding ElementName=userControl,Path=Text,Mode=TwoWay}" />
and remove handlers.

Ok, typical StackOverflow usage pattern adopted.
I realised I am writing this one-way, have added a TextChanged event handler to update the dependency property.
public partial class TemplateEditor : UserControl
{
public string Text
{
get
{
string s=(string)GetValue(TextProperty);
return s;
}
set
{
if (Text != value)
{
SetValue(TextProperty, value);
}
}
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(TemplateEditor),new PropertyMetadata(null,Text_PropertyChanged));
public TemplateEditor()
{
InitializeComponent();
textBox.TextChanged += new TextChangedEventHandler(textBox_TextChanged);
}
private static void Text_PropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
((TemplateEditor)source).textBox.Text = (string)e.NewValue;
}
void textBox_TextChanged(object sender, TextChangedEventArgs e)
{
Text = textBox.Text;
}
}
Thanks for your time, and apologies. :)

Related

How to bind to inkstrokes from the UWP InkCanvas hosted by WPF?

I am truly reaching here :) (I am a complete newbie to UWP/WinRt)
I am hosting a custom WinRT InkCanvas in a WPF Window. I am completely new to WinRt, so when it comes to loading or saving the inkstroke data, I am lost. The problem is getting/setting
the InkStrokes when they are burried within the InkContainer. I would like to use XAML binding from the WPF window to a backend F# module. (Elmish.wpf).
I currently do have both Elmish.wpf and the WinRT Inkcanvas working in the same application.
(Elmish.wpf runs fine with the new application interface required to host WinRT/UWP controls).
ANY HELP OR GUIDANCE WOULD BE MOST APPRECIATED!
So, not knowing how to get the inkstrokes, this is what I have so far:
My WPF container -- ProgressNoteWindow
Note the use of WindowsXamlHost. Somehow, I need the button go gain access to the inkstrokes contained in the InkContainer of Inkcanvas being hosted by the WindowsXamlHost!
<Window x:Class="Stargate.XI.Client.Views.ProgressNoteWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:xaml="clr-namespace:Microsoft.Toolkit.Wpf.UI.XamlHost;assembly=Microsoft.Toolkit.Wpf.UI.XamlHost"
xmlns:controls="clr-namespace:Microsoft.Toolkit.Wpf.UI.Controls;assembly=Microsoft.Toolkit.Wpf.UI.Controls"
xmlns:local="clr-namespace:Stargate.XI.Client.Views"
WindowStartupLocation="CenterScreen" WindowState="Maximized"
mc:Ignorable="d"
Title="ProgressNoteWindow" d:DesignHeight="1200" d:DesignWidth="1200">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<xaml:WindowsXamlHost x:Name="MyInkControl" Grid.Row="1" InitialTypeName="Loader2.MyUserControl2" ChildChanged="WindowsXamlHost_ChildChanged" />
<Button Command="{Binding Command}" CommandParameter="{Binding InkStrokes, ElementName=MyInkControl}" Content = "Save"/>
</Grid>
</Window>
My attached property --InkCanvasBinder
public static class InkCanvasBinder
{
public static InkStrokeContainer GetInkStrokes(DependencyObject obj) =>
obj.GetValue(InkStrokesProperty) as InkStrokeContainer;
public static void SetInkStrokes(DependencyObject obj, InkStrokeContainer value) =>
obj.SetValue(InkStrokesProperty, value);
public static DependencyProperty InkStrokesProperty = DependencyProperty.RegisterAttached(
"InkStrokes", typeof(InkStrokeContainer), typeof(InkCanvasBinder),
new PropertyMetadata(null, InkStrokesProperty_PropertyChanged));
private static void InkStrokesProperty_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var inkCanvas = d as InkCanvas;
if (inkCanvas != null) inkCanvas.InkPresenter.StrokeContainer = e.NewValue as InkStrokeContainer;
}
}
MyUserControl (in a UWP project):
<UserControl
x:Class="Loader2.MyUserControl2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Loader2"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="1200"
d:DesignWidth="1200">
<Grid Background="LightCoral">
<InkCanvas x:Name="TheInkCanvas" Loaded="InkCanvas_OnLoaded" local:InkCanvasBinder.InkStrokes = "{x:Bind MyInkStrokeContainer}"/>
<!-- Ensure the InkToolbar is declared after the InkCanvas. If not, the InkCanvas overlay renders the InkToolbar inaccessible., Mode=OneWay-->
<InkToolbar x:Name="myInkToolbar" TargetInkCanvas="{x:Bind TheInkCanvas}" Width="300" Height="50" Margin="10,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top" />
</Grid>
</UserControl>
MyUserControl - code-behind (in a UWP project)
using Windows.UI.Core;
using Windows.UI.Input.Inking;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
namespace Loader2
{
public sealed partial class MyUserControl2 : UserControl
{
public MyUserControl2()
{
this.InitializeComponent();
}
private void InkCanvas_OnLoaded(object sender, RoutedEventArgs e)
{
TheInkCanvas.InkPresenter.InputDeviceTypes = CoreInputDeviceTypes.Mouse | CoreInputDeviceTypes.Touch;
}
public InkStrokeContainer MyInkStrokeContainer
{
get { return (InkStrokeContainer)GetValue(MyInkStrokeContainerProperty); }
set { SetValue(MyInkStrokeContainerProperty, value); }
}
// Using a DependencyProperty as the backing store for MyInkStrokeContainer. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyInkStrokeContainerProperty =
DependencyProperty.Register("MyInkStrokeContainer", typeof(InkStrokeContainer), typeof(MyUserControl2), new PropertyMetadata(null));
}
}

Multibinding and binding to view model

Let's say this code sets label's content:
<Label.Content>
<MultiBinding Converter="{StaticResource converter}">
<Binding ElementName="EmailTextBox" Path="(Validation.Errors)"/>
<Binding ElementName="PhoneNumberTextBox" Path="(Validation.Errors)"/>
<Binding ElementName="MobileNumberTextBox" Path="(Validation.Errors)"/>
</MultiBinding>
</Label.Content>
And it works fine, but (in this case) is it possible to bind label's content to the view model, so the view model is immediately notified about label's content change?
Thanks in advance.
I came up solution and it seems to be working, you need to create your own Label, subsequently add DependencyProperty which will be assigned every time when the Content is changed. Unfortunately, there is no event like ContentChanged indicating that Content was changed so I had to add this in own Label as well. Take a look and let me know whether it works.
class MyLabel : Label
{
public static readonly DependencyProperty MyContentProperty = DependencyProperty.Register("MyContent", typeof(string), typeof(MyLabel));
public string MyContent
{
get { return (string)GetValue(MyContentProperty); }
set { SetValue(MyContentProperty, value); }
}
static MyLabel()
{
ContentProperty.OverrideMetadata(typeof(MyLabel),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnContentChanged)));
}
private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyLabel obj = d as MyLabel;
if (obj != null)
obj.MyContent = obj.Content.ToString();
}
}
and XAML looks as follows
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication3" WindowStartupLocation="CenterScreen"
Title="MainWindow" Height="350" Width="525">
<local:MyLabel Content="Content" MyContent="{Binding Zmienna, Mode=OneWayToSource}"/>
Now in property Zmienna you have your Content value.

Binding to custom dependency property - again

The task: implement the simplest Dependency Property ever, which can be used in xaml like that:
<uc:MyUserControl1 MyTextProperty="{Binding Text}"/>
I think that this answer is quite close. For better readability i copy all my code here (mostly from that answer above).
<UserControl x:Class="Test.UserControls.MyUserControl1"
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"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<!-- Text is being bound to outward representative property;
Note the DataContext of the UserControl -->
<TextBox Text="{Binding MyTextProperty}"/>
</Grid>
</UserControl>
and
public partial class MyUserControl1 : UserControl
{
// The dependency property which will be accessible on the UserControl
public static readonly DependencyProperty MyTextPropertyProperty =
DependencyProperty.Register("MyTextProperty", typeof(string), typeof(MyUserControl1), new UIPropertyMetadata(String.Empty));
public string MyTextProperty
{
get { return (string)GetValue(MyTextPropertyProperty); }
set { SetValue(MyTextPropertyProperty, value); }
}
public MyUserControl1()
{
InitializeComponent();
}
}
And this is my MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:uc="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<StackPanel Orientation="Vertical">
<uc:MyUserControl1 MyTextProperty="my text goes here"/>
<Button Click="ButtonBase_OnClick" Content="click"/>
</StackPanel>
</Window>
So far, everything works. However, i find this quite not usefull. What i'd need is
<uc:MyUserControl1 MyTextProperty="{Binding Text}"/>
and being able to change this by setting a DataContext (as you usually do in MVVM)
So i replace the line as above and add my code behind as follows:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
Text = "Initial Text";
DataContext = this;
}
private string _Text;
public string Text
{
get { return _Text; }
set
{
if (value != _Text)
{
_Text = value;
NotifyPropertyChanged("Text");
}
}
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Text = "clicked";
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
Neither the "initial Text" nor the "clicked" is displayed... ever. So my question is how to implement a dept. property correctly to be used with
<uc:MyUserControl1 MyTextProperty="{Binding Text}"/>
The Text property is located on the DataContext of the MainWindow not of the UserControl.
So change this line <uc:MyUserControl1 MyTextProperty="{Binding Text}"/> into this:
<uc:MyUserControl1 MyTextProperty="{Binding Text, ElementName=MyMainWindow}"/>
Which will tell the Binding that you're talking about the Text element located in you MainWindow. Of course, since in this example I used ElementName, you're going to want to name your window MyMainWindow...
So add this to your MainWindow:
<Window Name="MyMainWindow" ..... />
If you rather not name your window, you can use the RelativeSource FindAncestor binding like this:
<wpfApplication6:MyUserControl1 MyTextProperty="{Binding Text, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"/>
In both ways, you are asking to find the property named 'Text' in the DataContext of the window.

How to bind local property on control in WPF

I have two controls on WPF
<Button HorizontalAlignment="Center"
Name="btnChange"
Click="btnChange_Click"
Content="Click Me" />
<Label Name="lblCompanyId"
HorizontalAlignment="Center"
DataContext="{Binding ElementName=_this}"
Content="{Binding Path=CompanyName}" />
As we can see that label is bound to local property(in code Behind), I don't see any value on label when I click button...
Below is my code behind...
public static readonly DependencyProperty CompanyNameProperty =
DependencyProperty.Register("CompanyName", typeof(string), typeof(Window3), new UIPropertyMetadata(string.Empty));
public string CompanyName {
get { return (string)this.GetValue(CompanyNameProperty); }
set { this.SetValue(CompanyNameProperty, value); }
}
private void btnChange_Click(object sender, RoutedEventArgs e) {
this.CompanyName = "This is new company from code beind";
}
Try:
Content="{Binding ElementName=_this, Path=CompanyName}"
Without the DataContext binding.
EDIT
I have no problems with your code, have named your window to x:Name="_this"?
<Window x:Class="WpfStackOverflowSpielWiese.Window3"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window3"
Height="300"
Width="300"
x:Name="_this">
<Grid>
<StackPanel>
<Button HorizontalAlignment="Center"
Name="btnChange"
Click="btnChange_Click"
Content="Click Me" />
<Label Name="lblCompanyId"
HorizontalAlignment="Center"
DataContext="{Binding ElementName=_this}"
Content="{Binding Path=CompanyName}"></Label>
</StackPanel>
</Grid>
</Window>
Is your window really Window3?
public partial class Window3 : Window
{
public Window3() {
this.InitializeComponent();
}
public static readonly DependencyProperty CompanyNameProperty =
DependencyProperty.Register("CompanyName", typeof(string), typeof(Window3), new UIPropertyMetadata(string.Empty));
public string CompanyName {
get { return (string)this.GetValue(CompanyNameProperty); }
set { this.SetValue(CompanyNameProperty, value); }
}
private void btnChange_Click(object sender, RoutedEventArgs e) {
this.CompanyName = "This is new company from code behind";
}
}
You are currently binding your Label's DataContext to a Button, and then trying to set it's Content to CompanyName, however CompanyName is not a valid property on Button
Specify DataContext in your binding Path to bind to Button.DataContext.CompanyName instead of Button.CompanyName
Also, I'd recommend just binding the Content instead of binding both the DataContext and Content
<Label Content="{Binding ElementName=btnChange, Path=DataContext.CompanyName}" />
And if your code looks exactly like the code sample posted, then both the Button and Label have the same DataContext, so you can bind directly to CompanyName
<Label Content="{Binding CompanyName}" />
Edit
Just noticed that your Label's binding was to a control named _this. I had assumed it was the Button, although I see now that your Button's name is btnChange, not _this.
It doesn't matter though, the answer is still the same. You're trying to bind to a UI Control's CompanyName property, which is not a valid property.

Silverlight for Windows Phone: BindingExpression path error with user control - Property not found

Just hit a very odd issue with databinding which I cannot seem to get to the bottom of:
Scenario
An MVVM View model data bound to a parent form with two properties
public RelayCommand ClearFilteredCategories { get; private set; }
/// <summary>
/// The <see cref="ClearFilterText" /> property's name.
/// </summary>
public const string ClearFilterTextPropertyName = "ClearFilterText";
private string _clearFilterText = "Clear Filter";
/// <summary>
/// Sets and gets the ClearFilterText property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public string ClearFilterText
{
get
{
return _clearFilterText;
}
set
{
if (_clearFilterText == value)
{
return;
}
_clearFilterText = value;
RaisePropertyChanged(ClearFilterTextPropertyName);
}
}
Then I have a User control with Two dependency Properties, thus:
public partial class ClearFilterButton : UserControl
{
public ClearFilterButton()
{
// Required to initialize variables
InitializeComponent();
}
public string ClearFilterString
{
get { return (string)GetValue(ClearFilterStringProperty); }
set { SetValue(ClearFilterStringProperty, value); }
}
public RelayCommand ClearFilterAction
{
get { return (RelayCommand)GetValue(ClearFilterActionProperty); }
set { SetValue(ClearFilterActionProperty, value); }
}
public static readonly DependencyProperty ClearFilterStringProperty =
DependencyProperty.Register("ClearFilterString", typeof(string), typeof(ClearFilterButton), new PropertyMetadata("", ClearFilterString_PropertyChangedCallback));
public static readonly DependencyProperty ClearFilterActionProperty =
DependencyProperty.Register("ClearFilterAction", typeof(RelayCommand), typeof(ClearFilterButton), new PropertyMetadata(null, ClearFilterAction_PropertyChangedCallback));
private static void ClearFilterString_PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//empty
}
private static void ClearFilterAction_PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//empty
}
}
and User Control XAML:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:GalaSoft_MvvmLight_Command="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WP71"
mc:Ignorable="d"
x:Class="ATTCookBook.ClearFilterButton"
d:DesignWidth="75" d:DesignHeight="75"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid x:Name="LayoutRoot" Background="Transparent">
<Button HorizontalAlignment="Center" VerticalAlignment="Center" BorderBrush="{x:Null}" Width="75" Height="75">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding ClearFilterAction, Mode=TwoWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Button.Background>
<ImageBrush Stretch="UniformToFill" ImageSource="/icons/appbar.refresh.rest.png"/>
</Button.Background>
</Button>
<TextBlock HorizontalAlignment="Center" Margin="0,0,0,8" TextWrapping="Wrap" Text="{Binding ClearFilterString, Mode=TwoWay}" VerticalAlignment="Bottom" FontSize="13.333" Height="18" Width="0" d:LayoutOverrides="VerticalAlignment"/>
</Grid>
Now when I add this User Control to the Main Page and databind the two View Model Properties through to the User control it gets very weird:
<local:ClearFilterButton Height="Auto" Width="Auto" ClearFilterAction="{Binding ClearFilteredCategories, Mode=TwoWay}" ClearFilterString="{Binding ClearFilterText, Mode=TwoWay}"/>
Because although the Databinding statements above seem fine, the Binding errors with:
System.Windows.Data Error: BindingExpression path error: 'ClearFilteredCategories' property not found on 'ATTCookBook.ClearFilterButton' 'ATTCookBook.ClearFilterButton' (HashCode=126600431). BindingExpression: Path='ClearFilteredCategories' DataItem='ATTCookBook.ClearFilterButton' (HashCode=126600431); target element is 'ATTCookBook.ClearFilterButton' (Name=''); target property is 'ClearFilterAction' (type 'GalaSoft.MvvmLight.Command.RelayCommand')..
System.Windows.Data Error: BindingExpression path error: 'ClearFilterText' property not found on 'ATTCookBook.ClearFilterButton' 'ATTCookBook.ClearFilterButton' (HashCode=126600431). BindingExpression: Path='ClearFilterText' DataItem='ATTCookBook.ClearFilterButton' (HashCode=126600431); target element is 'ATTCookBook.ClearFilterButton' (Name=''); target property is 'ClearFilterString' (type 'System.String')..
Which seems to indicate the View Model is trying to find the parent properties in the child user control?
I do not understand why this could be because I have set a Relative Data Context within the child user control to avoid this and the binding should be passing through the two dependency properties.
I was hoping to make the user control more generic later but cannot seem to even get it working in a basic fashion
Quick call out to you Silverlight Binding masters :D
In your UserControl you are resetting the Datacontext so now the expression
ClearFilterAction="{Binding ClearFilteredCategories, Mode=TwoWay}"
is not relative to the Page where you are using the UserControl but to the UserControl itself. You can encounter the same problem with ItemTemplate datacontext when you want to reference a common command declared on the VM of the Page and not on the object data bound to the template.
You can use a proxy:
ClearFilterAction="{Binding Source={StaticResource
DataContextProxy},Path=DataSource.ClearFilteredCategories}"
The proxy is declared as a Resource:
<Resources:DataContextProxy x:Key="DataContextProxy" />
and the code of the DataProxy class:
public class DataContextProxy : FrameworkElement
{
public DataContextProxy()
{
this.Loaded += new RoutedEventHandler(DataContextProxy_Loaded);
}
void DataContextProxy_Loaded(object sender, RoutedEventArgs e)
{
var binding = new Binding();
if (!String.IsNullOrEmpty(BindingPropertyName))
{
binding.Path = new PropertyPath(BindingPropertyName);
}
binding.Source = this.DataContext;
binding.Mode = BindingMode;
this.SetBinding(DataContextProxy.DataSourceProperty, binding);
}
public Object DataSource
{
get { return (Object)GetValue(DataSourceProperty); }
set { SetValue(DataSourceProperty, value); }
}
public static readonly DependencyProperty DataSourceProperty =
DependencyProperty.Register("DataSource", typeof(Object), typeof(DataContextProxy), null);
public string BindingPropertyName { get; set; }
public BindingMode BindingMode { get; set; }
}
Move the assignment of the data context to usercontrol's root DataGrid.
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:GalaSoft_MvvmLight_Command="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WP71"
mc:Ignorable="d"
x:Class="ATTCookBook.ClearFilterButton"
d:DesignWidth="75" d:DesignHeight="75">
<Grid x:Name="LayoutRoot" Background="Transparent" DataContext="{Binding Parent, RelativeSource={RelativeSource Self}}" >
<Button HorizontalAlignment="Center" VerticalAlignment="Center" BorderBrush="{x:Null}" Width="75" Height="75">
Resolved in the end with some thanks to the above, however the answer was a lot simpler.
All I had to do was remove any mention of Modes (remove Mode="TwoWay") and remove the DataContext setting from the user control and it just worked.
Just goes to show it's very easy to over-engineer a solution.
I Suspect by adding the Mode setting it was trying to pass the datacontext through the binding and that what it was throwing up (not a very helpful error message)
Hope this helps others. Just keep it simple and work up from there.
For those that are interested, I was basing the implementation from this very useful post - Silverlight UserControl Custom Property Binding

Resources