DataBinding problems with custom UserControl - wpf

I have a strange issue when using DataBinding on a custom UserControl.
My UserControl "UserControl1" has a dependency property LabelText which sets the content of a label within my UserControl1. Furthermore, it has a button that binds the command "MyCommand". This command just shows a message box and is implemented in UserControl1ViewModel.
When I using UserControl1 in my MainWindow with also has its view model (MainWindowViewModel), I would like to set the UserControl's LabelText property in the MainWindow.xaml using a Binding to LabelTextFromMainWindow, but when I do it I have a problem that it uses the wrong DataContext unless you specify it explicitly.
This my code:
public partial class MainWindow : Window
{
private MainWindowViewModel vm;
public MainWindow()
{
InitializeComponent();
DataContext = vm = new MainWindowViewModel();
vm.LabelTextFromMainWindow = "Hallo";
}
}
class MainWindowViewModel : System.ComponentModel.INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this,
new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
#endregion
private string myLabel;
public string LabelTextFromMainWindow
{
get { return myLabel; }
set
{
myLabel = value;
OnPropertyChanged("MyLabel");
}
}
}
/////////
<UserControl x:Class="WpfApplication1.UserControl1"
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"
d:DesignHeight="224" d:DesignWidth="300">
<Grid>
<Button Command="{Binding MyCommand}" Content="Button" Height="55" HorizontalAlignment="Left" Margin="166,99,0,0" Name="button1" VerticalAlignment="Top" Width="104" />
<Label Margin="30,99,0,0" Name="label1" Height="55" VerticalAlignment="Top" HorizontalAlignment="Left" Width="101" />
</Grid>
</UserControl>
public partial class UserControl1 : UserControl
{
private UserControl1ViewModel vm;
private static UserControl1 instance;
public UserControl1()
{
InitializeComponent();
instance = this;
DataContext = vm = new UserControl1ViewModel();
}
public string LabelText
{
get { return (string)GetValue(LabelProperty); }
set { SetValue(LabelProperty, value); }
}
public static readonly DependencyProperty LabelProperty =
DependencyProperty.Register("LabelText", typeof(string), typeof(UserControl1), new UIPropertyMetadata(""), OnValidateValueProperty);
private static bool OnValidateValueProperty(object source)
{
if (instance != null)
{
instance.label1.Content = source;
}
return true;
}
}
public class UserControl1ViewModel
{
private DelegateCommand myCommand;
public ICommand MyCommand
{
get
{
if (myCommand == null)
myCommand = new DelegateCommand(new Action<object>(MyExecute),
new Predicate<object>(MyCanExecute));
return myCommand;
}
}
private bool MyCanExecute(object parameter)
{
return true;
}
private void MyExecute(object parameter)
{
MessageBox.Show("Hello World");
}
}
My mainwindow logs as followed:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:my="clr-namespace:WpfApplication1">
<Grid>
<my:UserControl1 LabelText="{Binding
Path=DataContext.LabelTextFromMainWindow,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}}"
HorizontalAlignment="Left"
Margin="114,36,0,0"
x:Name="userControl11"
VerticalAlignment="Top" Height="236" Width="292" />
</Grid>
</Window>
I exspected the following to work correctly.
LabelText="{Binding Path=LabelTextFromMainWindow}"
However, I have to write this one.
LabelText="{Binding Path=DataContext.LabelTextFromMainWindow,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}}"
What do I have to do in order get the simple Binding to work properly?

By default control inherits DataContext from its parent unless you set it explicitly.
In your case, you explicitly set the DataContext of your UserControl as
DataContext = vm = new UserControl1ViewModel();
which makes all the bindings on your UserControl to look for bindings in class UserControl1ViewModel instead in MainWindowViewModel.
That's why you have to use RelativeSource to get Window's DataContext i.e. you explicitly asked binding to be found in window's DataContext instead in its own DataContext and i see no issue in using RelativeSource.
But, if you want to work like simple binding without RelativeSource, first of all you need to get rid of explicitly setting DataContext and move all commands and properties in MainWindowsViewModel so that your UserControl inherits its DataContext from MainWindow.
OR
You can give name to your window and bind using ElementName -
<Window x:Class="WpfApplication1.MainWindow"
x:Name="MainWindow"> <--- HERE
<Grid>
<my:UserControl1 LabelText="{Binding
Path=DataContext.LabelTextFromMainWindow,
ElementName=MainWindow}"/>

Related

WPF Binding not refreshing if Equals and GetHashCode [duplicate]

I have a project, where I bind a checkbox's IsChecked property with a get/set in the codebehind. However, when the application loads, it doesn't update, for some reason. Intrigued, I stripped it down to its basics, like this:
//using statements
namespace NS
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private bool _test;
public bool Test
{
get { Console.WriteLine("Accessed!"); return _test; }
set { Console.WriteLine("Changed!"); _test = value; }
}
public MainWindow()
{
InitializeComponent();
Test = true;
}
}
}
XAML:
<Window x:Class="TheTestingProject_WPF_.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Viewbox>
<CheckBox IsChecked="{Binding Path=Test, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Viewbox>
</Grid>
And, lo and behold, when I set it to true, it did not update!
Anyone can come up with a fix, or explain why?
Thanks, it'd be appreciated.
In order to support data binding, your data object must implement INotifyPropertyChanged
Also, it's always a good idea to Separate Data from Presentation
public class ViewModel: INotifyPropertyChanged
{
private bool _test;
public bool Test
{ get { return _test; }
set
{
_test = value;
NotifyPropertyChanged("Test");
}
}
public PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
<Window x:Class="TheTestingProject_WPF_.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Viewbox>
<CheckBox IsChecked="{Binding Path=Test, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Viewbox>
</Grid>
Code Behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel{Test = true};
}
}

WPF DataContext inheritance from MainWindow to UserControl

I am a newbie in WPF, I have a problem concern DataContext inheritance from the MainWindow to a UserControl,
which will be attached as a Tabpage to the MainWindow's Tabcontrol.
My code snippets are as follows:
UserControlModel.cs
public class UserControlModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
OnPropertyChanged("Name");
}
}
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
ViewModelLocator.cs
public class ViewModelLocator
{
private UserControlModel UserControlModel { get; set; }
public ObservableCollection<UserControlModel> Users { get; set; }
public ViewModelLocator()
{
Users = new ObservableCollection<UserControlModel>
{
new UserControlModel { Name = "Albert" },
new UserControlModel { Name = "Brian" }
};
}
}
MainWindow.xaml
<Window.Resources>
<local:ViewModelLocator x:Key="VMLocator" />
</Window.Resources>
<Grid HorizontalAlignment="Left" Height="330" VerticalAlignment="Top" Width="592">
<Grid HorizontalAlignment="Left" Height="45" Margin="0,330,-1,-45" VerticalAlignment="Top" Width="593">
<Button Content="Button" HorizontalAlignment="Left" Margin="490,5,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
</Grid>
<TabControl HorizontalAlignment="Left" Height="330" VerticalAlignment="Top" Width="592" >
<TabItem x:Name="UserControlTabItem" Header="User Control">
<Grid x:Name="UserControlTabpage" Background="#FFE5E5E5">
<local:UserControl VerticalAlignment="Top" DataContext="{Binding Users, Source={StaticResource VMLocator}}" />
</Grid>
</TabItem>
</TabControl>
</Grid>
I create an instance of ViewModelLocator and bind Users instance to the UserControl in MainWindow.xaml.
MainWindow.xaml.cs
public MainWindow()
{
InitializeComponent();
}
UserControl.xaml
<Grid>
<ListBox x:Name="lbUsers" DisplayMemberPath="???" HorizontalAlignment="Left" Height="250" Margin="30,27,0,0" VerticalAlignment="Top" Width="378"/>
</Grid>
UserControl.xaml.cs
private ObservableCollection<UserControlModel> _users;
public UserControl()
{
InitializeComponent();
_users = ??? How to reference the Users instance created in MainWindow ???
lbUsers.ItemsSource = _users;
}
Actually, I want to show the Name property of UserControlModel in the ListBox. If I am right, UserControl instance is
inherited with a Users instance as the DataContext from MainWindow. How can I reference the Users instance in the code-behind
of UserControl.xaml.cs? I have checked that DataContext in UserControl constructor is null! How come? What is the
correct way/place to test the DataContext in the code-behind?
Also, how to set DisplayMemberPath attribute of the ListBox in UserControl.xaml. Many thanks.
I think you can set or inherit DataContext in XAML of user control like this
UserControl.xaml
<dialogs:Usercontrol DataContext="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext}" />
You need to bind the ItemsSource property of the ListBox to the source collection. Since the DataContext of the UserControl is a ObservableCollection<UserControlModel>, you can bind to it directly:
<ListBox x:Name="lbUsers" ItemsSource="{Binding}" DisplayMemberPath="Name" ... />
Also make sure that you don't explicitly set the DataContext of the UserControl anywhere else in your code.
Although you should be able to reference the DataContext once the UserControl has been loaded:
public UserControl()
{
InitializeComponent();
this.Loaded += (s, e) =>
{
_users = DataContext as ObservableCollection<UserControlModel>;
};
}
...there is no need to set the ItemsSource property of the ListBox in the code-behind. You should do this by creating a binding in the XAML.

WPF XAML - Property update in nested controls

I have a control that exposes a string property named HeaderText in this way:
public partial class HeaderControl : UserControl
{
public static DependencyProperty HeaderTextProperty;
[Category("Header Properties")]
public string HeaderText
{
get { return (string)GetValue(HeaderTextProperty); }
set { SetValue(HeaderTextProperty, value); }
}
static HeaderControl()
{
HeaderTextProperty = DependencyProperty.Register("HeaderText", typeof(string), typeof(HeaderControl), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
}
public HeaderControl()
{
this.DataContext = this;
InitializeComponent();
}
}
HeaderControl's Xaml:
<UserControl x:Class="Col.HMI.Controls.HeaderControl"
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"
d:DesignHeight="300" d:DesignWidth="300">
<Border Background="{Binding Path=HeaderBackground}" >
<TextBlock Text="{Binding Path=HeaderText}" Foreground="White" TextAlignment="Left" VerticalAlignment="Center" HorizontalAlignment="Stretch" FontFamily="Segoe UI Light" FontSize="36" Margin="5"/>
</Border>
and I want to use this HeaderControl in another UserControl, in this way:
OtherControl's Xaml:
<controls:HeaderControl Grid.Row="0" HeaderText="DEMO" />
And this works without problems. But if I bind the HeaderText property to a string property in the OtherControl ViewModel, in this way:
<controls:HeaderControl Grid.Row="0" HeaderText="{Binding Path=SummaryTitle}" />
the bind doesn't work.
This is the SummaryTitle property in the OtherControl ViewModel:
public string SummaryTitle
{
get
{
return _summaryTitle;
}
set
{
_summaryTitle = value; OnPropertyChanged("SummaryTitle");
}
}
PS: I have other controls binded to the OtherControl View Model and they work well.
You are setting DataContext of HeaderControl to itself in the constructor by doing this:
this.DataContext = this;
That means, when you apply some binding to any of the properties in HeaderControl, the Binding engine tries to find the bound property (in your case SummaryTitle) in this control, which it wont find and will fail.
So, to fix your problem, do not set the DataContext of HeaderControl to itself in the Constructor and the Binding engine will try find the properties in the correct DataContext.
Update your HeaderControl constructor to the following, and the bindings should start to work:
public HeaderControl()
{
InitializeComponent();
}
UPDATE
What you are trying to do here is, You want to have DependencyProperty named HeaderText in your UserControl, so that it's value can be set via DataBinding, and then update a value of TextBlock in your UserControl with the value of that DependencyProperty.
You can achieve this by two ways:
1) By updating TextBlock Binding to use ElementNme and Path syntax, XAML would look like this:
<UserControl x:Class="WpfApplication1.HeaderControl"
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"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="_This">
<Grid>
<TextBlock Text="{Binding ElementName=_This, Path=HeaderText}" FontSize="24" />
</Grid>
</UserControl>
With this approach, whenever the property HeaderText is changed either via Binding or explicitly setting the value.
2) By listening to property value changed event for HeaderText property and then updating the TextBlock accordingly.
For this approach your HeaderControl.xaml would look like:
<UserControl x:Class="WpfApplication1.HeaderControl"
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"
d:DesignHeight="300" d:DesignWidth="300" >
<Grid>
<TextBlock x:Name="TextBlockInUserControl"/>
</Grid>
</UserControl>
and the HeaderControl.xaml.cs
public partial class HeaderControl : UserControl
{
public static readonly DependencyProperty HeaderTextProperty;
[Category("Header Properties")]
public string HeaderText
{
get { return (string)GetValue(HeaderTextProperty); }
set { SetValue(HeaderTextProperty, value); }
}
static HeaderControl()
{
HeaderTextProperty = DependencyProperty.Register("HeaderText", typeof (string), typeof (HeaderControl),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnHeaderTextPropertyChanged));
}
private static void OnHeaderTextPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var headerControl = (HeaderControl) dependencyObject;
headerControl.UpdateTextBlock((string) dependencyPropertyChangedEventArgs.NewValue);
}
void UpdateTextBlock(string text)
{
TextBlockInUserControl.Text = text;
}
public HeaderControl()
{
InitializeComponent();
}
}

Cant access nested wpf item command by mvvm

Lets say i got a dummy WPF application (MVVM oriented).
My main window contains a custom List i created, and the list contains a custom item.
the item has an image button, and i want the button command to be the command i got in the viewmodel. the viewmodel is binded to the main window.
how can i do it ?
i attached the dummy project (download it here : http://www.2shared.com/file/qmO3E5rx/NestedCommand.html
or here : http://www.multiupload.nl/KCFLSKAIH0),
but if you don't want to download it,
the code goes like this :
MainWindow XAML:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Application="clr-namespace:WpfApplication2"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Application:List x:Name="myList" DataContext="{Binding}" />
</Grid>
MainWindow Code-Behind:
public MainWindow()
{
InitializeComponent();
CharacterViewModel viewModel = new CharacterViewModel();
this.myList.ItemsList.ItemsSource = viewModel.Model.Powers;
}
List XAML:
<UserControl x:Class="WpfApplication2.List"
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:Application="clr-namespace:WpfApplication2"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<ListView x:Name="ItemsList" ItemsSource="{Binding Path=Name}">
<ListView.ItemTemplate>
<DataTemplate>
<Application:Item x:Name="myItem" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
Item XAML:
<UserControl x:Class="WpfApplication2.Item"
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"
d:DesignHeight="50" d:DesignWidth="50">
<Grid>
<Button x:Name="ButtonImage" Command="**????????**">
<Button.Template>
<ControlTemplate>
<Border HorizontalAlignment="Center" VerticalAlignment="Center" >
<Image Width="50" Height="50" Source="/WpfApplication2;component/Images/Jellyfish.jpg"/>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
ViewModel Code :
public class CharacterViewModel : ObjectBase
{
public Character Model { get; private set; }
public DelegateCommand<object> RemoveCommand { get; private set; }
public CharacterViewModel()
: this(Character.Create())
{
}
public CharacterViewModel(Character model)
{
Model = model;
RemoveCommand = new DelegateCommand<object>(RemoveCommand_Execute, RemoveCommand_CanExecute, "Save");
}
void RemoveCommand_Execute(object arg)
{
Model.Powers.Clear();
MessageBox.Show(string.Format("{0} character powers removed.", Model.Name));
}
bool RemoveCommand_CanExecute(object arg)
{
return Model.Name != string.Empty;
}
}
Model Code:
public class Character : ObjectBase
{
string _Name = string.Empty;
ObservableCollection<string> _Powers = new ObservableCollection<string>();
public string Name
{
get { return _Name; }
set
{
if (_Name == value)
return;
_Name = value;
OnPropertyChanged("Name");
}
}
public ObservableCollection<string> Powers
{
get { return _Powers; }
}
public static Character Create()
{
Character hero = new Character()
{
Name = "Superman",
};
hero.Powers.Add("Flight");
hero.Powers.Add("Strength");
hero.Powers.Add("X-Ray Vision");
return hero;
}
}
Framework Code :
public class DelegateCommand<T> : ICommand
{
public DelegateCommand(Action<T> execute) : this(execute, null) { }
public DelegateCommand(Action<T> execute, Predicate<T> canExecute) : this(execute, canExecute, "") { }
public DelegateCommand(Action<T> execute, Predicate<T> canExecute, string label)
{
_Execute = execute;
_CanExecute = canExecute;
Label = label;
}
readonly Action<T> _Execute = null;
readonly Predicate<T> _CanExecute = null;
public string Label { get; set; }
public void Execute(object parameter)
{
_Execute((T)parameter);
}
public bool CanExecute(object parameter)
{
return _CanExecute == null ? true : _CanExecute((T)parameter);
}
public event EventHandler CanExecuteChanged
{
add
{
if (_CanExecute != null)
CommandManager.RequerySuggested += value;
}
remove
{
if (_CanExecute != null)
CommandManager.RequerySuggested -= value;
}
}
}
public abstract class ObjectBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected internal void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Thanks for helping
The DataContext for the ListItem is the item that it is bound to which is not what you're looking for. You're looking for the DataContext of the UserControl and to get that you'll need to either reference the UserControl explicitly using ElementName or use a RelativeSource binding to explore the visual tree. RelativeSource is probably the best solution and since it references the control itself you'll need to specify in the Path of the binding that you're looking for the RemoveCommand member on the DataContext - something like Path=DataContext.RemoveCommand. See full example below.
XAML:
<Grid DataContext="{Binding}"> <!-- Set the binding for the DataContext of the control and all of its children -->
<ListView ItemsSource="{Binding Path=Model.Powers}">
<ListView.ItemTemplate>
<DataTemplate>
<!-- Use RelativeSource to access the Grid control and then get its DataContext -->
<Button Command="{Binding Path=DataContext.RemoveCommand, RelativeSource={RelativeSource AncestorType=Grid}}">
<Border HorizontalAlignment="Center" VerticalAlignment="Center" >
<Image Width="50" Height="50" Source="/WpfApplication2;component/Images/Jellyfish.jpg"/>
</Border>
</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>

TwoWay Binding With ItemsControl

I'm trying to write a user control that has an ItemsControl, the ItemsTemplate of which contains a TextBox that will allow for TwoWay binding. However, I must be making a mistake somewhere in my code, because the binding only appears to work as if Mode=OneWay. This is a pretty simplified excerpt from my project, but it still contains the problem:
<UserControl x:Class="ItemsControlTest.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<Grid>
<StackPanel>
<ItemsControl ItemsSource="{Binding Path=.}"
x:Name="myItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Mode=TwoWay,
UpdateSourceTrigger=LostFocus,
Path=.}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Click="Button_Click"
Content="Click Here To Change Focus From ItemsControl" />
</StackPanel>
</Grid>
</UserControl>
Here's the code behind for the above control:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Collections.ObjectModel;
namespace ItemsControlTest
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public ObservableCollection<string> MyCollection
{
get { return (ObservableCollection<string>)GetValue(MyCollectionProperty); }
set { SetValue(MyCollectionProperty, value); }
}
// Using a DependencyProperty as the backing store for MyCollection. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyCollectionProperty =
DependencyProperty.Register("MyCollection",
typeof(ObservableCollection<string>),
typeof(UserControl1),
new UIPropertyMetadata(new ObservableCollection<string>()));
public UserControl1()
{
for (int i = 0; i < 6; i++)
MyCollection.Add("String " + i.ToString());
InitializeComponent();
myItemsControl.DataContext = this.MyCollection;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// Insert a string after the third element of MyCollection
MyCollection.Insert(3, "Inserted Item");
// Display contents of MyCollection in a MessageBox
string str = "";
foreach (string s in MyCollection)
str += s + Environment.NewLine;
MessageBox.Show(str);
}
}
}
And finally, here's the xaml for the main window:
<Window x:Class="ItemsControlTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:ItemsControlTest"
Title="Window1" Height="300" Width="300">
<Grid>
<src:UserControl1 />
</Grid>
</Window>
Well, that's everything. I'm not sure why editing the TextBox.Text properties in the window does not seem to update the source property for the binding in the code behind, namely MyCollection. Clicking on the button pretty much causes the problem to stare me in the face;) Please help me understand where I'm going wrong.
Thanx!
Andrew
Ok I believe what is causing this problem is that you are binding directly to a String . Strings are immutable in C# and thus when you change the text, it cannot change the underlying string in the ObservableCollection. What you can do to get around this problem is simply create a model class to hold the string data, and then bind the TextBox.Text to a property inside that class. Here is an example:
public partial class BindingToString : Window
{
public BindingToString()
{
MyCollection = new ObservableCollection<TestItem>();
for (int i = 0; i < 6; i++)
MyCollection.Add(new TestItem("String " + i.ToString()));
InitializeComponent();
myItemsControl.DataContext = this.MyCollection;
}
public ObservableCollection<TestItem> MyCollection
{
get;
set;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// Display contents of MyCollection in a MessageBox
string str = "";
foreach (TestItem s in MyCollection)
str += s.Name + Environment.NewLine;
MessageBox.Show(str);
}
}
public class TestItem
{
public string Name
{
get;
set;
}
public TestItem(string name)
{
Name = name;
}
}
Notice that I changed your dependency property to a standard property- there is no reason to make the collection a dependency property. Besides that the only difference is the inclusion of the wrapper class TestItem to hold the string data.
<Window x:Class="TestWpfApplication.BindingToString"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="BindingToString " Height="300" Width="300">
<Grid>
<StackPanel>
<ItemsControl ItemsSource="{Binding}"
x:Name="myItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Click="Button_Click"
Content="Click Here To Change Focus From ItemsControl" />
</StackPanel>
</Grid>
Now the TextBox is bound to the Name path on TestItem, and this binding works and modifies the collection as expected.

Resources