WPF user control binding dynamic combobox - wpf

I have a select list called Printers which is filled by default, after selecting a value from this select list the list called Trays should be filled but it doesn't. I've tried various methods but it seems to me that I still don't understand what the problem is. In addition, I would like to see a new list after pressing the Add next row button and it would also be full. I tried to use DependencyProperty and INotifyPropertyChanged but I don't think I fully understand the basics of the assumption or my problem is specific.
<UserControl x:Class="TestWPF.User_Controls.TrayItem"
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:local="clr-namespace:TestWPF.User_Controls"
mc:Ignorable="d"
Name="ucTray"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="White">
<StackPanel Margin="10" x:Name="spTray" Orientation ="Horizontal" Height="35" HorizontalAlignment="Center">
<Label VerticalAlignment="Center">Trays</Label>
<ComboBox Width="250" VerticalContentAlignment="Center">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ElementName=ucTray, Path=Trays }" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Label VerticalAlignment="Center">Documents</Label>
<ComboBox Width="250" VerticalContentAlignment="Center">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Documents , RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</Grid>
namespace TestWPF.User_Controls
{
public partial class TrayItem : UserControl
{
public List<string> Trays
{
get { return (List<string>)GetValue(TraysProperty); }
set { SetValue(TraysProperty, value); }
}
// Using a DependencyProperty as the backing store for Trays. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TraysProperty =
DependencyProperty.Register("Trays", typeof(List<string>), typeof(TrayItem), new PropertyMetadata(null));
public List<string> Documents
{
get { return (List<string>)GetValue(DocumentsProperty); }
set { SetValue(DocumentsProperty, value); }
}
// Using a DependencyProperty as the backing store for Documents. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DocumentsProperty =
DependencyProperty.Register("Documents", typeof(List<string>), typeof(TrayItem), new PropertyMetadata(null));
public TrayItem()
{
InitializeComponent();
}
}
}
<Window x:Class="TestWPF.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:TestWPF"
xmlns:control="clr-namespace:TestWPF.User_Controls"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<StackPanel Margin="10" x:Name="spItems">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Label>Printers</Label>
<ComboBox x:Name="cmbPrinterList" Width="300" SelectionChanged="cmbPrinterList_SelectionChanged" ItemsSource="{Binding MyPrinters}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
<control:TrayItem x:Name="cTray" Trays="{Binding MyTrays, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Documents="{Binding Path = MyDocuments, Mode=TwoWay}">
</control:TrayItem>
</StackPanel>
<Button x:Name="btnAddTray" Margin="10" Width="100" Height="40" Click="btnAddTray_Click" >Add next row</Button>
</Grid>
</Window>
namespace TestWPF
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
public List<string> MyPrinters { get; set; }
public string? _SelectedPrinter;
public string? SelectedPrinter
{
get
{
return _SelectedPrinter;
}
set
{
_SelectedPrinter = value;
}
}
private List<string>? _MyTrays;
public List<string>? MyTrays
{
get
{
return _MyTrays;
}
set
{
_MyTrays = value;
RaisePropertyChanged("SelectedPrinter");
RaisePropertyChanged("MyTrays");
RaisePropertyChanged("MyDocuments");
}
}
public List<string>? MyDocuments { get; set; }
public MainWindow()
{
InitializeComponent();
DataContext = this;
MyPrinters = new List<string>()
{
"Printer_1",
"Printer_2",
"Printer_3"
};
}
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler? PropertyChanged;
private void cmbPrinterList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string? value = (string?)cmbPrinterList.SelectedItem;
switch (value)
{
case "Printer_1":
MyTrays = new List<string>()
{
"aaa",
"bbb",
"ccc"
};
MyDocuments = new List<string>()
{
"zzz",
"qqq",
"rrr"
};
break;
case "Printer_2":
MyTrays = new List<string>()
{
"111",
"222",
"333"
};
MyDocuments = new List<string>()
{
"666",
"777",
"888"
};
break;
case "Printer_3":
MyTrays = new List<string>()
{
"1dg",
"b2f",
"bs4"
};
MyDocuments = new List<string>()
{
"b2vg",
"eb5",
"asc"
};
break;
}
SelectedPrinter = value;
}
private void btnAddTray_Click(object sender, RoutedEventArgs e)
{
TrayItem trayItem = new TrayItem { DataContext = cTray.DataContext };
spItems.Children.Add(trayItem);
}
}
}

Related

FrameworkPropertyMetadata.DefaultUpdateSourceTrigger doesn't set the default?

I have a UserControl for quantities called QtyControl, with a DependencyProperty called Qty (an int). I'm registering this property with DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged, but if I don't put UpdateSourceTrigger=PropertyChanged on the control consumer's binding, it doesn't work and I don't understand why.
The code should allow you to click the Add button and see whatever number you selected in the ComboBox, but always shows 0.
MainWindow.xaml:
<Window x:Class="UserControlTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:UserControlTest"
Title="MainWindow" Height="150" Width="250"
WindowStartupLocation="CenterScreen">
<StackPanel>
<DataGrid ItemsSource="{Binding MyItems}"
IsReadOnly="True"
Height="Auto" Width="Auto"
HeadersVisibility="Column"
AutoGenerateColumns="False"
SelectionMode="Single">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Qty" Width="50">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:QtyControl Qty="{Binding QtyRequested}" /> <!--, UpdateSourceTrigger=PropertyChanged - this is needed, but I don't know why when I registered Qty with DefaultUpdateSourceTrigger=PropertyChanged -->
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Add" Width="50">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Click="_AddItemBtn_Click">Add</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Window>
MainWindow.xaml.cs:
using System.Windows.Controls;
using System.Windows.Media;
namespace UserControlTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MyViewModel();
}
private void _AddItemBtn_Click(object sender, RoutedEventArgs e)
{
DataGridRow parentRow = _FindDataGridRowFromCl((Control)sender);
MyItem item = (MyItem)parentRow.Item;
MessageBox.Show($"QtyRequested = {item.QtyRequested}");
}
private DataGridRow _FindDataGridRowFromCl(Control cl)
{
for (Visual vi = cl as Visual; vi != null; vi = VisualTreeHelper.GetParent(vi) as Visual)
if (vi is DataGridRow row)
return row;
return null;
}
}
public class MyItem
{
public int QtyRequested { get; set; } = 0;
}
public class MyViewModel : INotifyPropertyChanged
{
private ObservableCollection<MyItem> _myItems;
public ObservableCollection<MyItem> MyItems {
get {
return _myItems;
}
set {
_myItems = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MyItems)));
}
}
public MyViewModel()
{
MyItems = new ObservableCollection<MyItem> { new MyItem() };
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
QtyControl.xaml:
<UserControl x:Class="UserControlTest.QtyControl"
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:local="clr-namespace:UserControlTest"
mc:Ignorable="d" Height="22" Width="42"
>
<Grid>
<ComboBox Name="_comboBox"
SelectedIndex="{Binding Qty, RelativeSource={RelativeSource AncestorType=UserControl}}">
<ComboBox.Items>
<ComboBoxItem Content="0" IsSelected="True" />
<ComboBoxItem Content="1" />
<ComboBoxItem Content="2" />
</ComboBox.Items>
</ComboBox>
</Grid>
</UserControl>
QtyControl.xaml.cs:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace UserControlTest
{
public partial class QtyControl : UserControl
{
public QtyControl()
{
InitializeComponent();
}
public static DependencyProperty QtyProperty;
static QtyControl()
{
QtyProperty = DependencyProperty.Register(
"Qty",
typeof(int),
typeof(QtyControl),
new FrameworkPropertyMetadata(
defaultValue: 1,
flags: FrameworkPropertyMetadataOptions.AffectsArrange,
propertyChangedCallback: null,
coerceValueCallback: null,
isAnimationProhibited: false,
defaultUpdateSourceTrigger: UpdateSourceTrigger.PropertyChanged
)
/* Also does not work
new FrameworkPropertyMetadata(
1,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault
)
*/
);
}
public int Qty
{
get { return (int)GetValue(QtyProperty); }
set { SetValue(QtyProperty, value); }
}
}
}

WPF MVVM and Observablecollect

I have a wpf application and I want to update my listview when I change the value through the UI using Observablecollect. But I don't get what I expect. When I change the value I won't update my list view.
View Code(Xaml)
<UserControl x:Class="DataWatch.View.CompareData"
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:ViewModels="clr-namespace:DataWatch.ViewModel"
mc:Ignorable="d"
d:DesignHeight="340" d:DesignWidth="600">
<UserControl.DataContext>
<ViewModels:CompareViewModel/>
</UserControl.DataContext>
<Grid>
<ListView HorizontalAlignment="Left" Name="comparelistview" VerticalAlignment="Top" Width="600" Height="340" ItemsSource="{Binding DisplayData}">
<ListView.View>
<GridView>
<GridViewColumn Width="150" Header="Key"
DisplayMemberBinding="{Binding Path=Key}" />
<GridViewColumn Width="150" Header="Project Data"
DisplayMemberBinding="{Binding Path=ProjectData}" />
<GridViewColumn Width="150" Header="Import Data"
DisplayMemberBinding="{Binding Path=ImportData}"/>
<GridViewColumn Width="150" Header="State"
DisplayMemberBinding="{Binding Path=State}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
ViewModel
namespace DataWatch.ViewModel
{
public class CompareViewModel
{
private ObservableCollection<CompareDiplayData> _displayData;
public CompareViewModel()
{
_displayData = new ObservableCollection<CompareDiplayData>();
}
public ObservableCollection<CompareDiplayData> DisplayData
{
get { return _displayData; }
}
}
Model:
namespace DataWatch.Model
{
public class CompareDiplayData : INotifyPropertyChanged
{
private string _key;
public string Key
{
set
{
_key = value;
this.Changed("Key");
}
get
{
return _key;
}
}
private string _projectData;
public string ProjectData
{
set
{
_projectData = value;
this.Changed("ProjectData");
}
get
{
return _projectData;
}
}
private string _importData;
public string ImportData
{
set
{
_importData = value;
this.Changed("ImportData");
}
get
{
return _importData;
}
}
private string _state;
public string State
{
set
{
_state = value;
this.Changed("State");
}
get
{
return _state;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void Changed(string PropertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
}
When I change the value in displayData ,but Listview won't update the data.
combobox view control
<UserControl x:Class="DataWatch.View.SelectPanel"
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:ViewModels="clr-namespace:DataWatch.ViewModel"
xmlns:AttachProperty="clr-namespace:DataWatch.AttachedProperty"
mc:Ignorable="d"
d:DesignHeight="30" d:DesignWidth="600">
<UserControl.DataContext>
<ViewModels:CompareViewModel/>
</UserControl.DataContext>
<Grid Name="good">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150">
</ColumnDefinition>
<ColumnDefinition Width="300">
</ColumnDefinition>
<ColumnDefinition Width="150">
</ColumnDefinition>
</Grid.ColumnDefinitions>
<ComboBox Height="23" HorizontalAlignment="Left" Grid.Column="0" Grid.Row="0" Margin="15,4,6,4" x:Name="KeyComboBox" VerticalAlignment="Top" Width="120" Text="Choose Key" AttachProperty:SelectionBehavior.SelectionChanged="{Binding SelectKeyCmd}" SelectedItem="{Binding SelectedKey, Mode=TwoWay}" ItemsSource="{Binding KeyComboboxItem}"
IsEditable="true" IsReadOnly="true"
IsDropDownOpen="True" StaysOpenOnEdit="True"/>
<ComboBox Height="23" HorizontalAlignment="Left" Grid.Column="1" Grid.Row="0" Margin="90,4,6,4" Name="compareitemcomBox" VerticalAlignment="Top" Width="120" Text="Compare Item" AttachProperty:SelectionBehavior.SelectionChanged="{Binding SelectKeyCmd}" SelectedItem="{Binding SelectedComparedData, Mode=TwoWay}" ItemsSource="{Binding CompareComboboxItem}"
IsEditable="true" IsReadOnly="true"
IsDropDownOpen="True" StaysOpenOnEdit="True"/>
<ComboBox Height="23" HorizontalAlignment="Left" Grid.Column="2" Grid.Row="0" Margin="15,4,6,4" Name="display" VerticalAlignment="Top" Width="120" Text="Choose State"
IsEditable="true" IsReadOnly="true"
IsDropDownOpen="True" StaysOpenOnEdit="True"/>
</Grid>
Attach property
public class SelectionBehavior
{
public static DependencyProperty SelectionChangedProperty =
DependencyProperty.RegisterAttached("SelectionChanged",
typeof(ICommand),
typeof(SelectionBehavior),
new UIPropertyMetadata(SelectionBehavior.SelectedItemChanged));
public static void SetSelectionChanged(DependencyObject target, ICommand value)
{
target.SetValue(SelectionBehavior.SelectionChangedProperty, value);
}
private static void SelectedItemChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
Selector element = target as Selector;
if (element == null) throw new InvalidOperationException("This behavior can be attached to Selector item only.");
if ((e.NewValue != null) && (e.OldValue == null))
{
element.SelectionChanged += SelectionChanged;
}
else if ((e.NewValue == null) && (e.OldValue != null))
{
element.SelectionChanged -= SelectionChanged;
}
}
private static void SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
UIElement element = (UIElement)sender;
ICommand command = (ICommand)element.GetValue(SelectionBehavior.SelectionChangedProperty);
command.Execute(((Selector)sender).SelectedValue);
}
}
viewmodel
public class CompareViewModel
{
private readonly ICommand _selectKeyCmd;
private List<string> _pro_Property;
private List<string> _imp_Property;
private string selectedKey;
private ObservableCollection<string> compareComboboxItem;
private DataTable _dt;
private CompareDiplayData domObject;
private ObservableCollection<CompareDiplayData> _displayData;
public CompareViewModel()
{
_displayData = new ObservableCollection<CompareDiplayData>();
_selectKeyCmd = new RelayCommand(ComboboxChanged, ComboboxIsChanged);
}
private void ReslutData()
{_displayData =null;
if (SelectedKey != null && SelectedComparedData != null)
{
foreach (DataRow pdr in _projectDt.Rows)
{
CompareDiplayData cdd = new CompareDiplayData();
foreach (DataRow idr in _importDt.Rows)
{
if (pdr[SelectedKey].ToString() == idr[SelectedKey].ToString())
{
cdd.Key = pdr[SelectedKey].ToString();
cdd.ProjectData = pdr[SelectedComparedData].ToString();
cdd.ImportData = idr[SelectedComparedData].ToString();
if (pdr[SelectedComparedData].ToString() == idr[SelectedComparedData].ToString())
cdd.State = "Match";
else
cdd.State = "Mismatch";
_displayData.Add(cdd);
}
}
}
}
}
public ObservableCollection<CompareDiplayData> DisplayData
{
get { return _displayData; }
}
public ICommand SelectKeyCmd
{
get { return _selectKeyCmd; }
}
private void ComboboxChanged(object obj)
{
ReslutData();
}
private bool ComboboxIsChanged(object obj)
{
return true;
}`}`
You are updating the reference of the Observablecollection here so that needs to be notified as the PropertyChange. In order to fix this, you will have to implement the INotifyPropertyChanged on your ViewModel also and write the setter of the DisplayData and raise property change for it.
public ObservableCollection<CompareDiplayData> DisplayData
{
get { return _displayData; }
set{_displayData = value;
this.Changed("DisplayData");
}
and in your ReslutData functin instead of update the variable _displayData, update the Property DisplayData.

WPF Data Templates and Buttons

Note: I am using Visual Studio 2012
I have the following DataTemplate (note: there is a TextBlock and Button)
<DataTemplate DataType="{x:Type ctlDefs:myButton}">
<StackPanel>
<TextBlock Text="{Binding LabelText}" Margin="10,0,10,0"/>
<Button Content="{Binding LabelText}" Margin="0,0,10,0"
Width="{Binding ButtonWidth}"
Height="35"
Command="{Binding ButtonCommand}"
Visibility="Visible"
/>
</StackPanel>
</DataTemplate>
My screen is dynamically adding controls of myButton to an Items Control
<ItemsControl ItemsSource="{Binding CommandButtons, Mode=OneWay}"
FlowDirection="LeftToRight"
DockPanel.Dock="Right"
>
The first time I render the view, the Textblock shows up for each button, but the Button itself does not. If I hide the view and then show it again, the button will appear sometimes (if the CommandButtons list does not change, if it changes, it never shows).
After rendering the view, I looked in the Output window and no binding errors.
What am I missing.
I've knocked up a quick application to work with your sample code and didn't seem to have any issues with the view. Below is what it looks like when run.
I've included my code below in case it helps. Note: I didn't add a real ICommand object for brevity and I accidentally named the MyButton class with capital M instead of small m like your example
ViewModelBase.cs
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
}
public class MainWindowViewModel : ViewModelBase
{
public MainWindowViewModel()
{
this.CommandButtons = new ObservableCollection<MyButton>();
}
public ObservableCollection<MyButton> CommandButtons { get; private set; }
}
App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var mainvm = new MainWindowViewModel();
var window = new MainWindow
{
DataContext = mainvm
};
window.Show();
mainvm.CommandButtons.Add(new MyButton { ButtonWidth = 50, LabelText = "Button 1" });
mainvm.CommandButtons.Add(new MyButton { ButtonWidth = 50, LabelText = "Button 2" });
mainvm.CommandButtons.Add(new MyButton { ButtonWidth = 50, LabelText = "Button 3" });
}
}
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:ctlDefs="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type ctlDefs:MyButton}">
<StackPanel>
<TextBlock Text="{Binding LabelText}" Margin="10,0,10,0"/>
<Button Content="{Binding LabelText}" Margin="0,0,10,0"
Width="{Binding ButtonWidth}"
Height="35"
Command="{Binding ButtonCommand}"
Visibility="Visible"
/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding CommandButtons, Mode=OneWay}"
FlowDirection="LeftToRight"
DockPanel.Dock="Right"
/>
</Grid>
MyButton.cs
public class MyButton : ViewModelBase
{
private string _labelText;
public string LabelText
{
get { return this._labelText; }
set { this._labelText = value; this.OnPropertyChanged("LabelText"); }
}
private double _buttonWidth;
public double ButtonWidth
{
get { return _buttonWidth; }
set { _buttonWidth = value; this.OnPropertyChanged("ButtonWidth"); }
}
private ICommand _buttonCommand;
public ICommand ButtonCommand
{
get { return _buttonCommand; }
set { _buttonCommand = value; this.OnPropertyChanged("ButtonCommand"); }
}
}

Listbox Binding Problem

Please solve my problem, my listbox don't bind, i don't why :(
class TLocation
{
public string name;
}
Main Window:
<Window x:Class="WpfApplication5.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication5"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Window.Resources>
<DataTemplate DataType="{x:Type local:TLocation}" x:Key="myTaskTemplate">
<TextBlock Height="23" HorizontalAlignment="Left" Margin="1" Name="textBlock1" Text="{Binding Path=name, FallbackValue=name}" VerticalAlignment="Top" />
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox Height="131" HorizontalAlignment="Left" Margin="29,44,0,0" Name="listBox1" VerticalAlignment="Top" Width="200" ItemTemplate="{StaticResource myTaskTemplate}" />
</Grid>
</Window>
Main Window Code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
List<TLocation> list = new List<TLocation>();
TLocation temp = new TLocation();
temp.name = "hi";
list.Add(temp);
listBox1.ItemsSource = list;
}
}
name is a field, you can only bind to public properties though. You might also want to implement INotifyPropertyChanged if the name changes at runtime. If you are new to databinding make sure to read the overview.
e.g.
public class TLocation : INotifyPropertyChanged
{
private string _name = null;
public string Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
OnPropertyChanged("Name");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
(Binding is also case senstitive and your capitalization is not following the conventions so i changed it in my example, here the Binding.Path would need to be Name.)

Data Binding : Child accessing AncestorType property

Bellow is the code behind and the Xaml for a demo app to review databing and wpf.
The problem is binding Store.ImagePath property to the person node is not working. That is the image is not showing.
<Image Source="{Binding Path=Store.ImagePath, RelativeSource={RelativeSource AncestorType={x:Type local:Store}}}" />
Here is the code-behind
namespace TreeViewDemo
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Customers customers = new Customers();
customers.Users = new List<Person>
{
new Person { Name = "John"},
new Person { Name = "Adam"},
new Person { Name = "Smith"}
};
Store store = new Store();
store.AllCustomers.Add(customers);
this.DataContext = store;
}
}
public class Store : INotifyPropertyChanged
{
string imagePath = "imageone.png";
public Store()
{
AllCustomers = new ObservableCollection<Customers>();
}
public string StoreName
{
get
{
return "ABC Store";
}
}
public ObservableCollection<Customers> AllCustomers
{
get;
set;
}
public string ImagePath
{
get
{
return imagePath;
}
set
{
if (value == imagePath) return;
imagePath = value;
this.OnPropertyChanged("ImagePath");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Customers
{
public string Label
{
get
{
return string.Format("People({0})", Users.Count());
}
}
public List<Person> Users
{
get;
set;
}
}
public class Person : INotifyPropertyChanged
{
public string Name
{
get;
set;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
and here is the Xaml.
<Window x:Class="TreeViewDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TreeViewDemo"
Title="MainWindow" Height="350" Width="525">
<Window.Resources >
<DataTemplate DataType="{x:Type local:Person}" x:Key="personKey" >
<StackPanel Orientation="Horizontal" >
<Image Source="{Binding Path=Store.ImagePath, RelativeSource={RelativeSource AncestorType={x:Type local:Store}}}" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
<HierarchicalDataTemplate x:Key="customerKey" ItemsSource="{Binding Users}" ItemTemplate="{StaticResource personKey }" >
<TextBlock Text="{Binding Label}" FontWeight="Bold"/>
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<Canvas>
<Button HorizontalAlignment="Left" DockPanel.Dock="Top" Height="29" Width="112" Canvas.Left="123" Canvas.Top="5">Image one</Button> <Button HorizontalAlignment="Left" VerticalAlignment="Top" DockPanel.Dock="Top" Height="28" Width="119" Canvas.Left="249" Canvas.Top="7">Image two</Button>
<TreeView HorizontalAlignment="Stretch" Name="treeView1" VerticalAlignment="Stretch"
ItemsSource="{Binding .}" Height="260" Width="363" Canvas.Left="81" Canvas.Top="45">
<TreeViewItem ItemsSource="{Binding AllCustomers}" ItemTemplate="{StaticResource customerKey}" Header="{Binding StoreName}"></TreeViewItem>
</TreeView>
</Canvas>
</Grid>
</Window>
All files are in the same directory.
Thanks
A relative source is used to look up an object in the visual tree. You're asking it to find the nearest Store in the visual tree. Since a Store cannot even be in the visual tree, the lookup will fail and yield null. What you actually want is the DataContext of the root Window, since that is where your Store is held:
<Image Source="{Binding DataContext.ImagePath, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />

Resources