Silverlight DataBinding MVVM - silverlight

Im having some problems with setting up databinding in Silverlight.
Im trying to use the MVVM approach and found some nice examples, So Ive created my View and my ViewModel, I created some classes Im going to use to contain the data and one to populate the classes.
Firstly my ViewModel looks like:
public class MainPageVM : INotifyPropertyChanged
{
ObservableCollection<Item> Items;
public MainPageVM()
{
InitializeItems InitItems = new InitializeItems();
InitItems.GenerateItemList(out Items);
RaiseProertyChanged("Items");
}
public string test = "Binding Test";
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
private void RaiseProertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
}
And then in my View i have :
<UserControl.Resources>
<viewmodel:MainPageVM x:Key="ViewModel" />
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource ViewModel}">
<StackPanel>
<TextBlock Text="{Binding test}"/>
<ListBox ItemsSource="{Binding Items}"
Width="200"
Height="200">
<ListBoxItem Width="190" Height="20">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding ItemName}"/>
<TextBlock Text="-"/>
<TextBlock Text="{Binding ItemID}"/>
</StackPanel>
</ListBoxItem>
</ListBox>
</StackPanel>
</Grid>
I added breakpoints and I know my ObservableCollection that im trying to bind to is being populated but nothing binds, in the error window im just getting xxx property doesnt exist in MainPageVM.
Any advice here would be great as im a bit lost as to what could be going on, and this is my first silverlight application.
Thanks

Items needs to be a public property. Same with your test field. In Silverlight you can only bind to public properties.
Also, typically in the Setter of those properties you raise the property changed event. This tells the Silverlight runtime to refresh the controls that are bound to that property with the new values of that property.

Related

C# WPF What's wrong with this template?

I'm trying to create a new window (child of MainWindow) that will display different numbers of rows with data each time. I used the following template that I copied from a wpf template tutorial. I've checked that the data are right and I can project them right with a MessageBox.Show window but I can't make them appear as bound properties. Here is the xaml...
<ListView x:Name="listUnits" x:FieldModifier="public" HorizontalAlignment="Left" Height="Auto" Margin="5,5,5,5" VerticalAlignment="Top" Width="Auto">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Id:"/>
<TextBlock Text="{Binding pu_Id}" Width="Auto"/>
<TextBlock Text=" "/>
<TextBlock Text="{Binding pu_unitName}" Width="Auto"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
So although pu_unitName contains the data, they're are displayed in the new window.
The binding is done in the constructor:
public partial class ChooseUnit : Window
{
public ChooseUnit(List<XML_Handler.PrUnits> thisList)
{
InitializeComponent();
listUnits.ItemsSource = thisList;
}
}
The PrUnits is a class which contains variables including pu_id and pu_unitName. So what's wrong with this code?
Since you can only bind to public properties, you need to make sure that pu_Id and pu_UnitName are properties (and not fields) of the PrUnits class. Then your bindings will work.
Also note that this is not a binding:
listUnits.ItemsSource = thisList;
In general, you would set the DataContext of the parent window to an instance of a view model class and bind the ItemsSource property of the ListView to a property that returns the List<XML_Handler.PrUnits>:
<ListView x:Name="listUnits" ItemsSource="{Binding Units}">
...
In your .cs:
public partial class ChooseUnit : Window
{
public ChooseUnit(List<XML_Handler.PrUnits> thisList)
{
InitializeComponent();
this.DataContext = thisList;
}
}
What this does it assigns the data context of your window to the list of itemsa you are trying to pass. At this point every element in that window will have access to the passed in list.
And then in your XAML do this:
<ListView x:Name="listUnits" ItemsSource="{Binding .}" x:FieldModifier="public" HorizontalAlignment="Left" Height="Auto" Margin="5,5,5,5" VerticalAlignment="Top" Width="Auto">
This allows us to use binding as the entire window has access to the list. Hence ItemsSource="{Binding .}".
EDIT
Reading more into your question and thanks to Ash your class needs to expose Properties with INotifyPropertyChanged interface implemented.
This interface is expected by the WPF framework so it can subscribe to the changed events and update the UI.

Getting weird behaviours when changing the binding to the TreeView

I have ObservableCollection Fathers which contains property ObservableCollection Sons.
And I'm displaying it on the TreeView setting its DataContext property.
The Sons property displays as a ListBox of radio button under each Father - binded to ItemsSource.
First time setting the DataContext of the tree view to the fathers list, everything is working good. The radio buttons are checked according to the data.
Now, I'm setting the TreeView.DataContext to null - so the data will disappear. and then back to the original Fathers ObservableCollection which I set in the first time.
And now from some reason the radio buttons stopped being synchronized with the son object.
And I got deeper and I saw that the setter in the son object (that binded to the radio button) is raised with false from some reason. I Guess something related to the binding.
Is there any cache that the TreeView, or the ObservableCollection is saving after binding ? I want it to work like the first time I set the bind - Which there like it should only the getter is being called like it should.
Thanks.
This is my Tree view
<UserControl x:Class="Tester.CTLMyTree"
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:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<Border Background="#FF919191" BorderThickness="1" CornerRadius="5"
HorizontalAlignment="Left" VerticalAlignment="Top"
Padding="5" BorderBrush="Black" Height="207" Width="190">
<Border.Resources>
<sdk:HierarchicalDataTemplate x:Key="LayerListTemplate">
<StackPanel Orientation="Vertical" Width="200" >
<TextBlock Text="Hello"/>
<ListBox x:Name="lstViews" ItemsSource="{Binding Sons}" BorderThickness="0" Width="200">
<ListBox.ItemTemplate>
<DataTemplate>
<RadioButton Content="Check" IsChecked="{Binding IsChecked, Mode=TwoWay}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</sdk:HierarchicalDataTemplate>
</Border.Resources>
<sdk:TreeView ItemsSource="{Binding}" ItemTemplate="{StaticResource LayerListTemplate}" x:Name="myTreeView" />
</Border>
</Grid>
</UserControl>
The Objects behind
public class CCFather
{
public CCFather()
{
Sons = new ObservableCollection<CCSon>();
}
public ObservableCollection<CCSon> Sons
{
get;
set;
}
}
public class CCSon
{
private bool m_blnChecked;
public bool IsChecked
{
get
{
return m_blnChecked;
}
set
{
m_blnChecked = value;
}
}
}
In my application i added this treeview control and called it m_objSimpleTree.
This code is the initializing
m_objItems = new ObservableCollection<CCFather>();
CCFather objItem1 = new CCFather();
objItem1.Sons.Add(new CCSon());
objItem1.Sons[0].IsChecked = true;
m_objItems.Add(objItem1);
m_objSimpleTree.myTreeView.DataContext = m_objItems;
And when i press a button i'm doing this
m_objSimpleTree.myTreeView.DataContext = null;
m_objSimpleTree.myTreeView.DataContext = m_objItems;
This code will raise already the IsChecked setter of the son to false (Why ???)
But the RadioButton will still be checked.
Second time pressing the button. it will be unchecked and the setter didn't raise.
When i'm pressing on the radio button It's raising twice the setter. First time with false
second with true.
Can't figure why it's happening.. The only think i can think of is that the treeview is saving something in the first binding or something like this.
It does so because you have used twoWay binding for the control
binding code project
In a two way binding when you change some thig on the view then the data gets saved in the object also . to which the datacontext is assigned. try oneWay for that . But need to be careful as if you wanna save data using twoWay one way might not help. MVVM suggests to use two way binding to save data but you want a refreshed list then create a new object :)
Also try clear Binding
Clear binding
Am not sure of the last link as never tried. Please o through it might get an idea. But since you might need a new object again so you can create a fresh object to assign to datacontext.
--- EDIT-----
Here's a xaml code
<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:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" x:Class="SilverlightSOApp.MainPage"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<Border Background="#FF919191" BorderThickness="1" CornerRadius="5"
HorizontalAlignment="Left" VerticalAlignment="Top"
Padding="5" BorderBrush="Black" Height="207" Width="190">
<Border.Resources>
<sdk:HierarchicalDataTemplate x:Key="LayerListTemplate">
<StackPanel Orientation="Vertical" Width="200" >
<TextBlock Text="Hello"/>
<ListBox x:Name="lstViews" ItemsSource="{Binding Sons}" BorderThickness="0" Width="200">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<RadioButton Content="Check" GroupName="abcd" IsChecked="{Binding IsChecked, Mode=TwoWay}"/>
<RadioButton Content="Check" GroupName="abcd" IsChecked="{Binding IsChecked2, Mode=TwoWay}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</sdk:HierarchicalDataTemplate>
</Border.Resources>
<sdk:TreeView ItemsSource="{Binding}" ItemTemplate="{StaticResource LayerListTemplate}" x:Name="myTreeView" />
</Border>
<Button Content="Button" HorizontalAlignment="Left" Margin="303,268,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
</Grid>
And the c#
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Browser;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace SilverlightSOApp
{
public partial class MainPage : UserControl
{
private ObservableCollection<CCFather> m_objItems;
public MainPage()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MainPage_Loaded);
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
m_objItems = new ObservableCollection<CCFather>();
CCFather objItem1 = new CCFather();
objItem1.Sons.Add(new CCSon());
objItem1.Sons[0].IsChecked = false;
m_objItems.Add(objItem1);
myTreeView.DataContext = m_objItems;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
myTreeView.DataContext = null;
myTreeView.DataContext = m_objItems;
}
}
public class CCFather
{
public CCFather()
{
Sons = new ObservableCollection<CCSon>();
}
public ObservableCollection<CCSon> Sons
{
get;
set;
}
}
public class CCSon
{
private bool m_blnChecked;
private bool m_blnChecked2;
public bool IsChecked
{
get
{
return m_blnChecked;
}
set
{
m_blnChecked = value;
}
}
public bool IsChecked2
{
get
{
return m_blnChecked2;
}
set
{
m_blnChecked2 = value;
}
}
}
}
Now the main point if you want to implement it for single radio button then you need to implement the Click event and set radio button to false and next time to true :) or else you need to use a checkbox one radio button once checked cannot be converted to false

Binding to a viewmodel property in a DataTemplate

I'm fairly new to XAML but enjoying learning it. The thing I'm really struggling with is binding a property to an element in a DataTemplate.
I have created a simple WPF example to, (hopefully,) explain my problem.
I this example I am trying to bind the Visibility property of a CheckBox in a DataTemplate to a Property in my viewmodel. (Using this scenario purely for learning/demo.)
I have a simple DataModel named Item, but is of little relevance in this example.
class Item : INotifyPropertyChanged
{
// Fields...
private bool _IsRequired;
private string _ItemName;
And a fairly simple View Model named ItemViewModel.
class ItemViewModel : INotifyPropertyChanged
{
private ObservableCollection<Item> _Items;
private bool _IsCheckBoxChecked;
private bool _IsCheckBoxVisible;
public ObservableCollection<Item> Items
{
get { return _Items; }
set { _Items = value; }
}
public bool IsCheckBoxChecked
{
get { return _IsCheckBoxChecked; }
set
{
if (_IsCheckBoxChecked == value)
return;
_IsCheckBoxChecked = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("IsCheckBoxChecked"));
PropertyChanged(this, new PropertyChangedEventArgs("IsCheckBoxVisible"));
}
}
}
public bool IsCheckBoxVisible
{
get { return !_IsCheckBoxChecked; }
set
{
if (_IsCheckBoxVisible == value)
return;
_IsCheckBoxVisible = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("IsCheckBoxVisible"));
}
(Constructors and INotifyPropertyChanged implementation omitted for brevity.)
Controls laid out in MainPage.xaml as follows.
<Window.Resources>
<local:VisibilityConverter x:Key="VisibilityConverter"/>
</Window.Resources>
<Window.DataContext>
<local:ItemViewModel/>
</Window.DataContext>
<Grid>
<StackPanel>
<CheckBox x:Name="checkBox" Content="Hide CheckBoxes" FontSize="14" IsChecked="{Binding IsCheckBoxChecked, Mode=TwoWay}" />
<ListView ItemsSource="{Binding Items}" HorizontalContentAlignment="Stretch" >
<ListView.ItemTemplate >
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding ItemName}"/>
<CheckBox Grid.Column="1" Visibility="{Binding IsCheckBoxVisible, Converter={StaticResource VisibilityConverter}}" >
<CheckBox.DataContext>
<local:ItemViewModel/>
</CheckBox.DataContext>
</CheckBox>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel Orientation="Horizontal" Margin="4,4,0,0">
<TextBlock Text="IsCheckBoxVisible:"/>
<TextBlock Text="{Binding IsCheckBoxVisible}" Margin="4,0,0,0" FontWeight="Bold" />
</StackPanel >
<Button Content="Button" Visibility="{Binding IsCheckBoxVisible, Converter={StaticResource VisibilityConverter}}" Margin="4,4,4,4"/>
</StackPanel>
</Grid>
The 'Hide CheckBoxes' checkbox is bound to IsCheckBoxChecked and is used to update IsCheckBoxVisible. I've also added a couple of extra controls below the DataTemplate to prove, (to myself,) the everything works.)
I have also implemented Jeff Wilcox's value converter. (Thank you.) http://www.jeff.wilcox.name/2008/07/visibility-type-converter/
When I run the app, checking and unchecking the 'Hide Checkbox', controls outside the DataTemplate function as expected but, alas, the Checkbox inside the data template remains unchanged.
I have had success with:
IsVisible="{Binding IsChecked, Converter={StaticResource VisibilityConverter}, ElementName=checkBox}"
But I'm not just trying mimic another control but make decisions based on a value.
I would REALLY appreciate any help or advice you can offer.
Thank you.
When you are in a DataTemplate, your DataContext is the data templated object, in this case an Item. Thus, the DataContext of the CheckBox in the DataTemplate is an Item, not your ItemViewModel. You can see this by your <TextBlock Text="{Binding ItemName}"/>, which binds to a property on the Item class. The Binding to IsCheckBoxVisible is trying to find a property called IsCheckBoxVisible on Item.
There are a couple of ways around this, but by far the easiest is to do this:
On your Window (in the xaml), give it and x:Name. Eg:
<Window [...blah blah...]
x:Name="MyWindow">
Change your binding to look like this:
<CheckBox Grid.Column="1"
Visibility="{Binding DataContext.IsCheckBoxVisible, ElementName=MyWindow, Converter={StaticResource VisibilityConverter}}">
We're using the Window as the source for the Binding, then looking at its DataContext property (which should be your ItemViewModel, and then pulling off the IsCheckBoxVisible property.
Another option, if you want something fancier, is to use a proxy object to reference your DataContext. See this article on DataContextProxy.

Custom UserControl property not being set via XAML DataBinding in Silverlight 4

I have a custom user control called GoalProgressControl. Another user control contains GoalProgressControl and sets its GoalName attribute via databinding in XAML. However, the GoalName property is never set. When I check it in debug mode GoalName remains "null" for the control's lifetime.
How do I set the GoalName property? Is there something I am doing incorrectly?
I am using .NET Framework 4 and Silverlight 4. I am relatively new to XAML and Silverlight so any help would be greatly appreciated.
I have attempted to change GoalProgressControl.GoalName into a POCO property but this causes a Silverlight error, and my reading leads me to believe that databound properties should be of type DependencyProperty. I've also simplified my code to just focus on the GoalName property (the code is below) with no success.
Here is GoalProgressControl.xaml:
<UserControl x:Class="GoalView.GoalProgressControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Height="100">
<Border Margin="5" Padding="5" BorderBrush="#999" BorderThickness="1">
<TextBlock Text="{Binding GoalName}"/>
</Border>
</UserControl>
GoalProgressControl.xaml.cs:
public partial class GoalProgressControl : UserControl, INotifyPropertyChanged
{
public GoalProgressControl()
{
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public static DependencyProperty GoalNameProperty = DependencyProperty.Register("GoalName", typeof(string), typeof(GoalProgressControl), null);
public string GoalName
{
get
{
return (String)GetValue(GoalProgressControl.GoalNameProperty);
}
set
{
base.SetValue(GoalProgressControl.GoalNameProperty, value);
NotifyPropertyChanged("GoalName");
}
}
}
I've placed GoalProgressControl on another page:
<Grid Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Margin="5" Background="#eee" Height="200">
<Border BorderBrush="#999" BorderThickness="1" Background="White">
<StackPanel>
<hgc:SectionTitleBar x:Name="ttlGoals" Title="Personal Goals" ImageSource="../Images/check.png" Uri="/Pages/GoalPage.xaml" MoreVisibility="Visible" />
<ItemsControl ItemsSource="{Binding Path=GoalItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<!--TextBlock Text="{Binding Path=[Name]}"/-->
<goal:GoalProgressControl GoalName="{Binding Path=[Name]}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Border>
</Grid>
Please note the commented out "TextBlock" item above. If I comment in the TextBlock and comment out the GoalProgressControl, the binding works correctly and the TextBlock shows the GoalName correctly. Also, if I replace the "GoalName" property above with a simple text string (ex "hello world"), the control renders correctly and "hello world" is shown on the control when it renders.
You have to pass
new PropertyMetadata(OnValueChanged)
as a last parameter to DependencyProperty.Register call and set the property
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((GoalProgressControl)d).GoalName = (String)e.NewValue;
}
I've written an article on Silverlight 4 Custom Property Binding that you might find useful, and it can be found here:
http://nick-howard.blogspot.com/2011/03/silverlight-4-custom-property-binding.html

WPF binding to Listbox selectedItem

Can anyone help with the following - been playing about with this but can't for the life of me get it to work.
I've got a view model which contains the following properties;
public ObservableCollection<Rule> Rules { get; set; }
public Rule SelectedRule { get; set; }
In my XAML I've got;
<ListBox x:Name="lbRules" ItemsSource="{Binding Path=Rules}"
SelectedItem="{Binding Path=SelectedRule, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Name:" />
<TextBox x:Name="ruleName">
<TextBox.Text>
<Binding Path="Name" UpdateSourceTrigger="PropertyChanged" />
</TextBox.Text>
</TextBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
Now the ItemsSource works fine and I get a list of Rule objects with their names displayed in lbRules.
Trouble I am having is binding the SelectedRule property to lbRules' SelectedItem. I tried binding a textblock's text property to SelectedRule but it is always null.
<TextBlock Text="{Binding Path=SelectedRule.Name}" />
The error I'm seeing in the output window is:
BindingExpression path error: 'SelectedRule' property not found.
Can anyone help me with this binding - I can't see why it shouldn't find the SelectedRule property.
I then tried changing the textblock's text property as bellow, which works. Trouble is I want to use the SelectedRule in my ViewModel.
<TextBlock Text="{Binding ElementName=lbRules, Path=SelectedItem.Name}" />
Thanks very much for your help.
First off, you need to implement INotifyPropertyChanged interface in your view model and raise the PropertyChanged event in the setter of the Rule property. Otherwise no control that binds to the SelectedRule property will "know" when it has been changed.
Then, your XAML
<TextBlock Text="{Binding Path=SelectedRule.Name}" />
is perfectly valid if this TextBlock is outside the ListBox's ItemTemplate and has the same DataContext as the ListBox.
Inside the DataTemplate you're working in the context of a Rule, that's why you cannot bind to SelectedRule.Name -- there is no such property on a Rule.
To bind to the original data context (which is your ViewModel) you can write:
<TextBlock Text="{Binding ElementName=lbRules, Path=DataContext.SelectedRule.Name}" />
UPDATE: regarding the SelectedItem property binding, it looks perfectly valid, I tried the same on my machine and it works fine. Here is my full test app:
XAML:
<Window x:Class="TestWpfApplication.ListBoxSelectedItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ListBoxSelectedItem" Height="300" Width="300"
xmlns:app="clr-namespace:TestWpfApplication">
<Window.DataContext>
<app:ListBoxSelectedItemViewModel/>
</Window.DataContext>
<ListBox ItemsSource="{Binding Path=Rules}" SelectedItem="{Binding Path=SelectedRule, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Name:" />
<TextBox Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>
Code behind:
namespace TestWpfApplication
{
/// <summary>
/// Interaction logic for ListBoxSelectedItem.xaml
/// </summary>
public partial class ListBoxSelectedItem : Window
{
public ListBoxSelectedItem()
{
InitializeComponent();
}
}
public class Rule
{
public string Name { get; set; }
}
public class ListBoxSelectedItemViewModel
{
public ListBoxSelectedItemViewModel()
{
Rules = new ObservableCollection<Rule>()
{
new Rule() { Name = "Rule 1"},
new Rule() { Name = "Rule 2"},
new Rule() { Name = "Rule 3"},
};
}
public ObservableCollection<Rule> Rules { get; private set; }
private Rule selectedRule;
public Rule SelectedRule
{
get { return selectedRule; }
set
{
selectedRule = value;
}
}
}
}
Yocoder is right,
Inside the DataTemplate, your DataContext is set to the Rule its currently handling..
To access the parents DataContext, you can also consider using a RelativeSource in your binding:
<TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ____Your Parent control here___ }}, Path=DataContext.SelectedRule.Name}" />
More info on RelativeSource can be found here:
http://msdn.microsoft.com/en-us/library/system.windows.data.relativesource.aspx
For me, I usually use DataContext together in order to bind two-depth property such as this question.
<TextBlock DataContext="{Binding SelectedRule}" Text="{Binding Name}" />
Or, I prefer to use ElementName because it achieves bindings only with view controls.
<TextBlock DataContext="{Binding ElementName=lbRules, Path=SelectedItem}" Text="{Binding Name}" />
There is a shorter version to bind to a selected item's property:
<TextBlock Text="{Binding Rules/Name}" />
since you set your itemsource to your collection, your textbox is tied to each individual item in that collection. the selected item property is useful in this scenario if you were trying to do a master-detail form, having 2 listboxes. you would bind the second listbox's itemsource to the child collection of rules. in otherwords the selected item alerts outside controls that your source has changed, internal controls(those inside your datatemplate already are aware of the change.
and to answer your question yes in most circumstances setting the itemsource is the same as setting the datacontext of the control.

Resources