Binding Data to a UserControl hosted by a DataTemplate - wpf

I have a user control which exposes a property which is a long. I'd like to instantiate this control and bind to the exposed property in a data template.
I'm seeing xaml errors in the resource file. The ambiguous "must have derivative of panel as the root element". And when I run this in a debugger, I see that the value of TeamIdx is -1 and is not being set.
<DataTemplate x:Key="TeamScheduleTemplate">
<Grid HorizontalAlignment="Left" Width="400" Height="600">
<Team:ScheduleControl TeamIdx="{Binding Idx}" />
</Grid>
</DataTemplate>
public sealed partial class ScheduleControl : UserControl
{
public static readonly DependencyProperty TeamIdxProperty =
DependencyProperty.Register(
"TeamIdx",
typeof(long),
typeof(ScheduleControl),
new PropertyMetadata((long)-1));
public long TeamIdx
{
get { return (long)GetValue(TeamIdxProperty); }
set { SetValue(TeamIdxProperty, value); }
}
public ScheduleControl()
{
this.InitializeComponent();
var team = TeamLookup.GetTeam(TeamIdx);
}
}

Edit: It turns out that the binding doesn't happen until after the control is constructed. In retrospect, this makes total sense. The solution I used is below:
public sealed partial class ScheduleControl : UserControl
{
public static readonly DependencyProperty TeamIdxProperty =
DependencyProperty.Register(
"TeamIdx",
typeof(long),
typeof(ScheduleControl),
new PropertyMetadata(
(long)-1,
OnTeamIdxChanged));
public long TeamIdx
{
get { return (long)GetValue(TeamIdxProperty); }
set { SetValue(TeamIdxProperty, value); }
}
private static void OnTeamIdxChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var target = (ScheduleControl)sender;
target.OnTeamIdxChanged((long)e.NewValue);
}
private void OnTeamIdxChanged(long id)
{
var model = FindModel(id);
this.DataContext = model;
}
public ScheduleControl()
{
this.InitializeComponent();
}
}

Related

WPF: Evaluate XAML binding beyond top level custom object

In my xaml I declare a custom object which has a property referencing another custom object:
public class CustomObjectA : FrameworkElement
{
public CustomObjectB InnerObj
{
get { return GetValue(InnerObjProperty); }
set { SetValue(InnerObjProperty, value); }
}
public static readonly DependencyProperty InnerObjProperty =
DependencyProperty.Register("InnerObj",
typeof(CustomObjectB),
typeof(CustomObjectA),
new PropertyMetadata(null));
}
public class CustomObjectB : FrameworkElement
{
public string Data
{
get { return GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data",
typeof(string),
typeof(CustomObjectB),
new PropertyMetadata(null));
}
Now when I declare these objects in my xaml like this the binding doesn't work:
<my:CustomObjectA>
<my:CustomObjectA.InnerObj>
<my:CustomObjectB Data="{Binding someValue}" />
</my:CustomObjectA.InnerObj>
</my:CustomObjectA>
However when I declare these objects in my xaml like this the binding works:
<my:CustomObjectB x:Name="testObj" Data="{Binding someValue}" />
<my:CustomObjectA InnerObj="{Binding ElementName=testObj}" />
I'm assuming this is because the system mapping the bindings doesn't look past the top level object. My question is; is there a way to tell the system to evaluate binding expressions beyond the top level custom object so xaml like option 1 will work?
The DataContext is not passed down to the InnerObj. I got it to work by updating the DataContext when the InnerObj changes:
public class CustomObjectA : FrameworkElement
{
public CustomObjectB InnerObj
{
get { return (CustomObjectB)GetValue(InnerObjProperty); }
set { SetValue(InnerObjProperty, value); }
}
public static readonly DependencyProperty InnerObjProperty =
DependencyProperty.Register("InnerObj",
typeof(CustomObjectB),
typeof(CustomObjectA),
new FrameworkPropertyMetadata(OnInnerObjUpdated));
private static void OnInnerObjUpdated(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var local = (CustomObjectA)d;
var newObj = (CustomObjectB)e.NewValue;
newObj.DataContext = local.DataContext;
}
}

WPF UserControl with a url as dependencyproperty

I'm creating a wpf usercontrol which is basically a button that's supposed to open a document. I want to create it so anyone can be able to put the url of this document in the xaml. Is this possible?
Edit: I've added a dependencyproperty to store the url, but it throws an exception whenever I try to build it. The xaml looks like this:
<controls:HelpButton WidthAndHeight="40" HelpDocUrl="somUrl"/>
and my code behind for the property looks like this:
public string HelpDocUrl
{
get { return (string)GetValue(HelpDocUrlProperty); }
set { SetValue(HelpDocUrlProperty, value); }
}
public static readonly DependencyProperty HelpDocUrlProperty = DependencyProperty.Register("HelpDocUrl", typeof(string), typeof(HelpButton), new PropertyMetadata(default(string)));
Add a dependency property of the code-behind class of your UserControl:
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public static readonly DependencyProperty UrlProperty =
DependencyProperty.Register("Url", typeof(string), typeof(UserControl1), new PropertyMetadata(null));
public string Url
{
get { return (string)GetValue(UrlProperty); }
set { SetValue(UrlProperty, value); }
}
private void Button_Click(object sender, RoutedEventArgs e)
{
string url = Url;
//...
}
}
...that any consumer of your control can set as usual:
<local:UserControl1 Url="http://...." />

How to bind dependency property to UI for silverlight user control?

I tried to create a user control as:
public partial class MyTextBlock : UserControl
{
public MyTextBlock()
{
InitializeComponent();
}
public static readonly DependencyProperty LabelProperty
= DependencyProperty.RegisterAttached("Label", typeof(string), typeof(MyTextBlock), null);
public string Label
{
get { return (string)GetValue(LabelProperty); }
set { SetValue(LabelProperty, value); }
}
public static readonly DependencyProperty MyTextProperty
= DependencyProperty.RegisterAttached("MyText", typeof(string), typeof(MyTextBlock), null);
public string MyText
{
get { return (string)GetValue(MyTextProperty); }
set { SetValue(MyTextProperty, value); }
}
}
And its xaml is:
<Grid x:Name="LayoutRoot">
<TextBlock x:Name="Title" Text="{Binding Label}" />
<TextBlock x:Name="MyText" Text="{Binding MyText}" TextWrapping="Wrap"/>
</Grid>
Want I want is trying to binding dependency property in this control to UI elements, so that when i use this control, I can set data binding like:
<local:MyTextBlock Label="{Binding ....}" MyText = "{Binding ....}" />
But When I did as above, it's not working. No data bound, no error. How to fix it?
Trying using .Register instead of .RegisterAttached on the DependencyProperty
You need to provide a callback to set the value
I think the 'int' type should be 'string'
putting it all together
public partial class MyTextBlock : UserControl
{
public MyTextBlock()
{
InitializeComponent();
}
public static readonly DependencyProperty LabelProperty
= DependencyProperty.Register("Label", typeof(string), typeof(MyTextBlock), new PropertyMetadata(new PropertyChangedCallback(LabelChanged)));
public string Label
{
get { return (string)GetValue(LabelProperty); }
set { SetValue(LabelProperty, value); }
}
private static void LabelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var c = d as MyTextBlock;
if (c != null )
{
c.label.Text = e.NewValue as string;
}
}
}
Basically you just have to wrap those dependency properties in a class. Set the DataContext on your control to an instance of that class and bind away.

OneWayToSource binding from readonly property in XAML

I'm trying to bind to a Readonly property with OneWayToSource as mode, but it seems this cannot be done in XAML:
<controls:FlagThingy IsModified="{Binding FlagIsModified,
ElementName=container,
Mode=OneWayToSource}" />
I get:
The property 'FlagThingy.IsModified' cannot be set because it does not have an accessible set accessor.
IsModified is a readonly DependencyProperty on FlagThingy. I want to bind that value to the FlagIsModified property on the container.
To be clear:
FlagThingy.IsModified --> container.FlagIsModified
------ READONLY ----- ----- READWRITE --------
Is this possible using just XAML?
Update: Well, I fixed this case by setting the binding on the container and not on the FlagThingy. But I'd still like to know if this is possible.
Some research results for OneWayToSource...
Option # 1.
// Control definition
public partial class FlagThingy : UserControl
{
public static readonly DependencyProperty IsModifiedProperty =
DependencyProperty.Register("IsModified", typeof(bool), typeof(FlagThingy), new PropertyMetadata());
}
<controls:FlagThingy x:Name="_flagThingy" />
// Binding Code
Binding binding = new Binding();
binding.Path = new PropertyPath("FlagIsModified");
binding.ElementName = "container";
binding.Mode = BindingMode.OneWayToSource;
_flagThingy.SetBinding(FlagThingy.IsModifiedProperty, binding);
Option # 2
// Control definition
public partial class FlagThingy : UserControl
{
public static readonly DependencyProperty IsModifiedProperty =
DependencyProperty.Register("IsModified", typeof(bool), typeof(FlagThingy), new PropertyMetadata());
public bool IsModified
{
get { return (bool)GetValue(IsModifiedProperty); }
set { throw new Exception("An attempt ot modify Read-Only property"); }
}
}
<controls:FlagThingy IsModified="{Binding Path=FlagIsModified,
ElementName=container, Mode=OneWayToSource}" />
Option # 3 (True read-only dependency property)
System.ArgumentException: 'IsModified' property cannot be data-bound.
// Control definition
public partial class FlagThingy : UserControl
{
private static readonly DependencyPropertyKey IsModifiedKey =
DependencyProperty.RegisterReadOnly("IsModified", typeof(bool), typeof(FlagThingy), new PropertyMetadata());
public static readonly DependencyProperty IsModifiedProperty =
IsModifiedKey.DependencyProperty;
}
<controls:FlagThingy x:Name="_flagThingy" />
// Binding Code
Same binding code...
Reflector gives the answer:
internal static BindingExpression CreateBindingExpression(DependencyObject d, DependencyProperty dp, Binding binding, BindingExpressionBase parent)
{
FrameworkPropertyMetadata fwMetaData = dp.GetMetadata(d.DependencyObjectType) as FrameworkPropertyMetadata;
if (((fwMetaData != null) && !fwMetaData.IsDataBindingAllowed) || dp.ReadOnly)
{
throw new ArgumentException(System.Windows.SR.Get(System.Windows.SRID.PropertyNotBindable, new object[] { dp.Name }), "dp");
}
....
This is a limitation of WPF and it is by design. It is reported on Connect here:
OneWayToSource binding from a readonly dependency property
I made a solution to dynamically be able to push read-only dependency properties to the source called PushBinding which I blogged about here. The example below does OneWayToSource Bindings from the read-only DP's ActualWidth and ActualHeight to the Width and Height properties of the DataContext
<TextBlock Name="myTextBlock">
<pb:PushBindingManager.PushBindings>
<pb:PushBinding TargetProperty="ActualHeight" Path="Height"/>
<pb:PushBinding TargetProperty="ActualWidth" Path="Width"/>
</pb:PushBindingManager.PushBindings>
</TextBlock>
PushBinding works by using two Dependency Properties, Listener and Mirror. Listener is bound OneWay to the TargetProperty and in the PropertyChangedCallback it updates the Mirror property which is bound OneWayToSource to whatever was specified in the Binding.
Demo Project can be Downloaded Here.
It contains source code and short sample usage.
Wrote this:
Usage:
<TextBox Text="{Binding Text}"
p:OneWayToSource.Bind="{p:Paths From={x:Static Validation.HasErrorProperty},
To=SomeDataContextProperty}" />
Code:
using System;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
public static class OneWayToSource
{
public static readonly DependencyProperty BindProperty = DependencyProperty.RegisterAttached(
"Bind",
typeof(ProxyBinding),
typeof(OneWayToSource),
new PropertyMetadata(default(Paths), OnBindChanged));
public static void SetBind(this UIElement element, ProxyBinding value)
{
element.SetValue(BindProperty, value);
}
[AttachedPropertyBrowsableForChildren(IncludeDescendants = false)]
[AttachedPropertyBrowsableForType(typeof(UIElement))]
public static ProxyBinding GetBind(this UIElement element)
{
return (ProxyBinding)element.GetValue(BindProperty);
}
private static void OnBindChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((ProxyBinding)e.OldValue)?.Dispose();
}
public class ProxyBinding : DependencyObject, IDisposable
{
private static readonly DependencyProperty SourceProxyProperty = DependencyProperty.Register(
"SourceProxy",
typeof(object),
typeof(ProxyBinding),
new PropertyMetadata(default(object), OnSourceProxyChanged));
private static readonly DependencyProperty TargetProxyProperty = DependencyProperty.Register(
"TargetProxy",
typeof(object),
typeof(ProxyBinding),
new PropertyMetadata(default(object)));
public ProxyBinding(DependencyObject source, DependencyProperty sourceProperty, string targetProperty)
{
var sourceBinding = new Binding
{
Path = new PropertyPath(sourceProperty),
Source = source,
Mode = BindingMode.OneWay,
};
BindingOperations.SetBinding(this, SourceProxyProperty, sourceBinding);
var targetBinding = new Binding()
{
Path = new PropertyPath($"{nameof(FrameworkElement.DataContext)}.{targetProperty}"),
Mode = BindingMode.OneWayToSource,
Source = source
};
BindingOperations.SetBinding(this, TargetProxyProperty, targetBinding);
}
public void Dispose()
{
BindingOperations.ClearAllBindings(this);
}
private static void OnSourceProxyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
d.SetCurrentValue(TargetProxyProperty, e.NewValue);
}
}
}
[MarkupExtensionReturnType(typeof(OneWayToSource.ProxyBinding))]
public class Paths : MarkupExtension
{
public DependencyProperty From { get; set; }
public string To { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
var provideValueTarget = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
var targetObject = (UIElement)provideValueTarget.TargetObject;
return new OneWayToSource.ProxyBinding(targetObject, this.From, this.To);
}
}
Have not tested it in styles and templates yet, guess it needs special casing.
Here is another implementation for binding to Validation.HasError
public static class OneWayToSource
{
public static readonly DependencyProperty BindingsProperty = DependencyProperty.RegisterAttached(
"Bindings",
typeof(OneWayToSourceBindings),
typeof(OneWayToSource),
new PropertyMetadata(default(OneWayToSourceBindings), OnBinidngsChanged));
public static void SetBindings(this FrameworkElement element, OneWayToSourceBindings value)
{
element.SetValue(BindingsProperty, value);
}
[AttachedPropertyBrowsableForChildren(IncludeDescendants = false)]
[AttachedPropertyBrowsableForType(typeof(FrameworkElement))]
public static OneWayToSourceBindings GetBindings(this FrameworkElement element)
{
return (OneWayToSourceBindings)element.GetValue(BindingsProperty);
}
private static void OnBinidngsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((OneWayToSourceBindings)e.OldValue)?.ClearValue(OneWayToSourceBindings.ElementProperty);
((OneWayToSourceBindings)e.NewValue)?.SetValue(OneWayToSourceBindings.ElementProperty, d);
}
}
public class OneWayToSourceBindings : FrameworkElement
{
private static readonly PropertyPath DataContextPath = new PropertyPath(nameof(DataContext));
private static readonly PropertyPath HasErrorPath = new PropertyPath($"({typeof(Validation).Name}.{Validation.HasErrorProperty.Name})");
public static readonly DependencyProperty HasErrorProperty = DependencyProperty.Register(
nameof(HasError),
typeof(bool),
typeof(OneWayToSourceBindings),
new FrameworkPropertyMetadata(default(bool), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
internal static readonly DependencyProperty ElementProperty = DependencyProperty.Register(
"Element",
typeof(UIElement),
typeof(OneWayToSourceBindings),
new PropertyMetadata(default(UIElement), OnElementChanged));
private static readonly DependencyProperty HasErrorProxyProperty = DependencyProperty.RegisterAttached(
"HasErrorProxy",
typeof(bool),
typeof(OneWayToSourceBindings),
new PropertyMetadata(default(bool), OnHasErrorProxyChanged));
public bool HasError
{
get { return (bool)this.GetValue(HasErrorProperty); }
set { this.SetValue(HasErrorProperty, value); }
}
private static void OnHasErrorProxyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
d.SetCurrentValue(HasErrorProperty, e.NewValue);
}
private static void OnElementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue == null)
{
BindingOperations.ClearBinding(d, DataContextProperty);
BindingOperations.ClearBinding(d, HasErrorProxyProperty);
}
else
{
var dataContextBinding = new Binding
{
Path = DataContextPath,
Mode = BindingMode.OneWay,
Source = e.NewValue
};
BindingOperations.SetBinding(d, DataContextProperty, dataContextBinding);
var hasErrorBinding = new Binding
{
Path = HasErrorPath,
Mode = BindingMode.OneWay,
Source = e.NewValue
};
BindingOperations.SetBinding(d, HasErrorProxyProperty, hasErrorBinding);
}
}
}
Usage in xaml
<StackPanel>
<TextBox Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}">
<local:OneWayToSource.Bindings>
<local:OneWayToSourceBindings HasError="{Binding HasError}" />
</local:OneWayToSource.Bindings>
</TextBox>
<CheckBox IsChecked="{Binding HasError, Mode=OneWay}" />
</StackPanel>
This implementation is specific to binding Validation.HasError
Here's another attached property solution based on SizeObserver detailed here Pushing read-only GUI properties back into ViewModel
public static class MouseObserver
{
public static readonly DependencyProperty ObserveProperty = DependencyProperty.RegisterAttached(
"Observe",
typeof(bool),
typeof(MouseObserver),
new FrameworkPropertyMetadata(OnObserveChanged));
public static readonly DependencyProperty ObservedMouseOverProperty = DependencyProperty.RegisterAttached(
"ObservedMouseOver",
typeof(bool),
typeof(MouseObserver));
public static bool GetObserve(FrameworkElement frameworkElement)
{
return (bool)frameworkElement.GetValue(ObserveProperty);
}
public static void SetObserve(FrameworkElement frameworkElement, bool observe)
{
frameworkElement.SetValue(ObserveProperty, observe);
}
public static bool GetObservedMouseOver(FrameworkElement frameworkElement)
{
return (bool)frameworkElement.GetValue(ObservedMouseOverProperty);
}
public static void SetObservedMouseOver(FrameworkElement frameworkElement, bool observedMouseOver)
{
frameworkElement.SetValue(ObservedMouseOverProperty, observedMouseOver);
}
private static void OnObserveChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var frameworkElement = (FrameworkElement)dependencyObject;
if ((bool)e.NewValue)
{
frameworkElement.MouseEnter += OnFrameworkElementMouseOverChanged;
frameworkElement.MouseLeave += OnFrameworkElementMouseOverChanged;
UpdateObservedMouseOverForFrameworkElement(frameworkElement);
}
else
{
frameworkElement.MouseEnter -= OnFrameworkElementMouseOverChanged;
frameworkElement.MouseLeave -= OnFrameworkElementMouseOverChanged;
}
}
private static void OnFrameworkElementMouseOverChanged(object sender, MouseEventArgs e)
{
UpdateObservedMouseOverForFrameworkElement((FrameworkElement)sender);
}
private static void UpdateObservedMouseOverForFrameworkElement(FrameworkElement frameworkElement)
{
frameworkElement.SetCurrentValue(ObservedMouseOverProperty, frameworkElement.IsMouseOver);
}
}
Declare attached property in control
<ListView ItemsSource="{Binding SomeGridItems}"
ut:MouseObserver.Observe="True"
ut:MouseObserver.ObservedMouseOver="{Binding IsMouseOverGrid, Mode=OneWayToSource}">
WPF will not use the CLR property setter, but seems it does some odd validation based on it.
May be in your situation this can be ok:
public bool IsModified
{
get { return (bool)GetValue(IsModifiedProperty); }
set { throw new Exception("An attempt ot modify Read-Only property"); }
}
Hmmm... I'm not sure I agree with any of these solutions. How about specifying a coercion callback in your property registration that ignores external change? For instance, I needed to implement a read-only Position dependency property to get the position of a MediaElement control inside a user control. Here's how I did it:
public static readonly DependencyProperty PositionProperty = DependencyProperty.Register("Position", typeof(double), typeof(MediaViewer),
new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal, OnPositionChanged, OnPositionCoerce));
private static void OnPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctrl = d as MediaViewer;
}
private static object OnPositionCoerce(DependencyObject d, object value)
{
var ctrl = d as MediaViewer;
var position = ctrl.MediaRenderer.Position.TotalSeconds;
if (ctrl.MediaRenderer.NaturalDuration.HasTimeSpan == false)
return 0d;
else
return Math.Min(position, ctrl.Duration);
}
public double Position
{
get { return (double)GetValue(PositionProperty); }
set { SetValue(PositionProperty, value); }
}
In other words, simply ignore the change and return the value backed by a different member that does not have a public modifier. -- In the above example, MediaRenderer is actually the private MediaElement control.
The way I worked around this limitation was to expose only a Binding property in my class, keeping the DependencyProperty private altogether. I implemented a "PropertyBindingToSource" write-only property (this one not a DependencyProperty) which can be set to a binding value in the xaml. In the setter for this write-only property I call to BindingOperations.SetBinding to link the binding to the DependencyProperty.
For the OP's specific example, it would look like this:
The FlatThingy implementation:
public partial class FlatThingy : UserControl
{
public FlatThingy()
{
InitializeComponent();
}
public Binding IsModifiedBindingToSource
{
set
{
if (value?.Mode != BindingMode.OneWayToSource)
{
throw new InvalidOperationException("IsModifiedBindingToSource must be set to a OneWayToSource binding");
}
BindingOperations.SetBinding(this, IsModifiedProperty, value);
}
}
public bool IsModified
{
get { return (bool)GetValue(IsModifiedProperty); }
private set { SetValue(IsModifiedProperty, value); }
}
private static readonly DependencyProperty IsModifiedProperty =
DependencyProperty.Register("IsModified", typeof(bool), typeof(FlatThingy), new PropertyMetadata(false));
private void Button_Click(object sender, RoutedEventArgs e)
{
IsModified = !IsModified;
}
}
Notice that the static readonly DependencyProperty object is private. In the control I added a button whose click is handled by Button_Click.
The use of the FlatThingy control in my window.xaml:
<Window x:Class="ReadOnlyBinding.MainWindow"
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:local="clr-namespace:ReadOnlyBinding"
mc:Ignorable="d"
DataContext="{x:Static local:ViewModel.Instance}"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="{Binding FlagIsModified}" Grid.Row="0" />
<local:FlatThingy IsModifiedBindingToSource="{Binding FlagIsModified, Mode=OneWayToSource}" Grid.Row="1" />
</Grid>
Note that I've also implemented a ViewModel for binding to that is not shown here. It exposes a DependencyProperty named "FlagIsModified" as you can glean from the source above.
It works great, allowing me to push information back into the ViewModel from the View in a loosely coupled manner, with the direction of that information flow explicitly defined.
You're doing the binding in the wrong direction right now. OneWayToSource will try and update FlagIsModified on container whenever IsModified changes on the control you are creating. You want the opposite, which is to have IsModified bind to container.FlagIsModified. For that you should use the binding mode OneWay
<controls:FlagThingy IsModified="{Binding FlagIsModified,
ElementName=container,
Mode=OneWay}" />
Full list of enumeration members: http://msdn.microsoft.com/en-us/library/system.windows.data.bindingmode.aspx

Silverlight 2.0 - databinding a domain object to a UserControl

I am starting out with Silverlight. I want to display a list of messages on the UI, but the databinding isn't working for me.
I have a Message class:
public class Message
{
public string Text { get; set; }
...
}
I have the message display Silverlight User control with a Message dependency property:
public partial class MessageDisplay : UserControl
{
public static readonly DependencyProperty MessageProperty =
DependencyProperty.Register("Message", typeof(Message),
typeof(MessageDisplay), null);
public MessageDisplay()
{
InitializeComponent();
}
public Message Message
{
get
{
return (Message)this.GetValue(MessageProperty);
}
set
{
this.SetValue(MessageProperty, value);
this.DisplayMessage(value);
}
}
private void DisplayMessage(Message message)
{
if (message == null)
{
this.MessageDisplayText.Text = string.Empty;
}
else
{
this.MessageDisplayText.Text = message.Text;
}
}
}
}
Then in the main control xaml I have
<ListBox x:Name="MessagesList" Style="{StaticResource MessagesListBoxStyle}">
<ListBox.ItemTemplate>
<DataTemplate>
<Silverbox:MessageDisplay Message="{Binding}"></Silverbox:MessageDisplay>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox
And I bind in the control.xaml.cs code:
this.MessagesList.SelectedIndex = -1;
this.MessagesList.ItemsSource = this.messages;
Databinding gives no error, and it seems that there are the right number of items in the list, but a breakpoint in MessageDisplay's Message property settor is never hit, and the message is never displayed properly.
What have I missed?
Your Message property is probably being set by the databinding which is bypassing your actual Message property (not the dependency one). To fix this add a PropertyChangedCallback on that property.
public static readonly DependencyProperty MessageProperty =
DependencyProperty.Register("Message", typeof(Message), typeof(MessageDisplay),
new PropertyMetadata(
new PropertyChangedCallback(MessageDisplay.MessagePropertyChanged));
public static void MessagePropertyChanged(DependencyObject obj, DependecyPropertyChangedEventArgs e)
{
((MessageDisplay)obj).Message = (Message)e.NewValue;
}
PropertyMetadata
PropertyChangedCallback

Resources