Silverlight 5 ListBox IsSelected style binding broken? - silverlight

i see Silverlight 5 bought style binding. Tried to apply it in a ListBox control, for multiple selection. I have the following XAML ListBox (the code works in a WPF application).
<ListBox ItemsSource="{Binding Values}" SelectionMode="Multiple">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate DataType="ListBoxItem">
<TextBlock Text="{Binding DisplayValue}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When i run this i get a binding error, it seems that it tries to find the IsSelected property on the type of "Values" collection instead of each individual item from that collection. Has anyone else experience this?
Update
Added full code to reproduce, you need to scroll the listbox to see the error in the output log
public class ValueViewModel : INotifyPropertyChanged
{
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
OnPropertyChanged("IsSelected");
}
}
private string _displayValue;
public string DisplayValue
{
get { return _displayValue; }
set
{
_displayValue = value;
OnPropertyChanged("DisplayValue");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class MainPageViewModel : INotifyPropertyChanged
{
public ObservableCollection<ValueViewModel> _values;
public ObservableCollection<ValueViewModel> Values
{
get { return _values; }
set
{
_values = value;
OnPropertyChanged("Values");
}
}
public MainPageViewModel()
{
Values = new ObservableCollection<ValueViewModel>();
for (var i = 0; i < 50; i++)
Values.Add(new ValueViewModel() { DisplayValue = i.ToString(), IsSelected = (i % 5) == 0 });
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And the XAML:
<Grid x:Name="LayoutRoot" Background="White" >
<Grid.Resources>
<viewmodels:MainPageViewModel x:Key="vmMainPage"/>
</Grid.Resources>
<Grid x:Name="workGrid" DataContext="{Binding Source={StaticResource vmMainPage}}">
<ListBox ItemsSource="{Binding Values}" SelectionMode="Multiple" Height="100">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Margin="5" Text="{Binding DisplayValue}"/>
<TextBlock Margin="5" Text="{Binding IsSelected}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
Update 2
It seems the problem around the error is that in a scrollable situation if you select items 1 and then scroll down and select item 49 (in the above example), the 1st selection is lost.

I cannot reproduce it. It works fine for me. Here is a full working example based on your code. One issue that I did notice though is that when a ListBoxItem is rendered it automatically sets the property on the data object to false, regardless of whether it was true to begin with. So if you load up a list and set some of it's items to be pre-selected, all the items will be unselected when the ListBoxItems are rendered. One way to prevent this is to use Dispatcher.BeginInvoke and set the selected items there. See my comments in the code below.
XAML:
<UserControl x:Class="SilverlightApplication12.MainPage"
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"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid x:Name="LayoutRoot"
Background="White">
<ListBox ItemsSource="{Binding Entities}"
SelectionMode="Multiple">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected"
Value="{Binding Path=IsSelected, Mode=TwoWay}" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate DataType="ListBoxItem">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
<TextBlock Margin="10 0 0 0"
Text="IsSelected:" />
<TextBlock Margin="5 0 0 0"
Text="{Binding IsSelected}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
Code-behind + entity class:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Windows;
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 SilverlightApplication12
{
public partial class MainPage : UserControl, INotifyPropertyChanged
{
private ObservableCollection<MyEntity> _Entities;
public ObservableCollection<MyEntity> Entities
{
get { return _Entities; }
set
{
_Entities = value;
OnPropertyChanged("Entities");
}
}
public MainPage()
{
InitializeComponent();
Entities = new ObservableCollection<MyEntity>();
Entities.Add(new MyEntity()
{
Name = "One",
IsSelected = false,
});
Entities.Add(new MyEntity()
{
Name = "Two",
IsSelected = true,
//Even though this is initially true it does not matter.
//When the ListBoxItem is rendered it sets the property to false.
});
Entities.Add(new MyEntity()
{
Name = "Three",
IsSelected = false,
});
LayoutRoot.DataContext = this;
//Enable the following line to set the 2nd item to selected when the page is loaded.
//Dispatcher.BeginInvoke(() => Entities[1].IsSelected = true);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class MyEntity : INotifyPropertyChanged
{
private string _Name;
public string Name
{
get { return _Name; }
set
{
_Name = value;
OnPropertyChanged("Name");
}
}
private bool _IsSelected;
public bool IsSelected
{
get
{
return _IsSelected;
}
set
{
_IsSelected = value;
OnPropertyChanged("IsSelected");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}

Everything except this <DataTemplate DataType="ListBoxItem"> looks fine to me.
If the Values collection is a collection of ListBoxItems, you don't need the IsSelected binding.
Otherwise, the DataType on your DataTemplate is wrong and should probably be left blank.

So i've managed to find a workarround that seems to do the job for my needs. It will set the already loaded values once the Loaded event gets triggered. And it wraps the MouseDown event to set the selection status. It's not a true databind but gets the job done, and still keeps the View clean of code.
<ListBox ItemsSource="{Binding Values}" SelectionMode="Multiple" Height="100">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Margin="2" Text="{Binding DisplayValue, Mode=TwoWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<ei:ChangePropertyAction
TargetObject="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
PropertyName="IsSelected"
Value="{Binding IsSelected}"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeftButtonDown">
<ei:ChangePropertyAction
TargetObject="{Binding}"
PropertyName="IsSelected"
Value="{Binding IsSelected, Converter={StaticResource invertBooleanConverter}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Related

Change button image when context menu item is selected

Can anyone tell me how to change button image when context menu Item is clicked?
I have a button with image and context menu in it. I want to change the image of the button, everytime I click contextmenu item.
With the following piece of code I am able to display contextmenu items on right click. But don't know how to proceed further.
Can anyone guide me ?
I tried using command strangely command never got called.
<Button Background="Gray" Name="statusBtn" VerticalAlignment="Top" HorizontalAlignment="Right" FontWeight="Bold" Foreground="Red">
<DockPanel >
<Image DockPanel.Dock="Top" Stretch="Fill" Source="{Binding ToEnum, Converter={StaticResource EnumToImgConverter}}" Height="37" Width="72" />
<TextBlock HorizontalAlignment="Center" Margin="0,23,1,2">Test</TextBlock>
</DockPanel>
<Button.ContextMenu>
<ContextMenu Name="ContextMenuName" ItemsSource="{Binding Path=Popuplistitems}">
<ContextMenu.ItemTemplate >
<DataTemplate DataType="MenuItem">
<MenuItem Header="{Binding Message}" Command="{Binding popupListCommand}">
<MenuItem.ItemContainerStyle >
<Style TargetType="MenuItem">
<Setter Property="IsCheckable" Value="True"/>
<Setter Property="IsChecked" Value="{Binding Path=IsSelected, Mode=TwoWay}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</Button.ContextMenu>
</Button>
First, I think you need to change your MenuItem.ItemContainerStyle to just MenuItem.Style - I believe the ItemContainerStyle is meant for the sub-menuitems that you can place into the MenuItem when you want nested context menus.
I was able to get the content of the button to change with the following code. I used text content, but it should be easy to swap it out for image content:
The xaml:
<Window x:Class="ChangeButtonContent.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>
<Button Height="30" Width="200">
<Button.Content>
<DockPanel >
<TextBlock Text="{Binding ChangingText, Mode=TwoWay}" />
</DockPanel>
</Button.Content>
<Button.ContextMenu>
<ContextMenu Name="ContextMenuName">
<MenuItem Command="{Binding ChangeTextCommand}" Header="ChangeText"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
</Grid>
The Code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
ChangingText = "Initial Text";
ChangeTextCommand = new MyCommand((notUsed) =>
{
ChangingText = "Text After Context Menu Click";
});
}
private string _changingText;
public string ChangingText
{
get
{
return _changingText;
}
set
{
_changingText = value;
OnPropertyChanged("ChangingText");
}
}
public MyCommand ChangeTextCommand
{
get;
private set;
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class MyCommand : ICommand
{
private Action<object> _action;
public MyCommand(Action<object> action)
{
_action = action;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action(parameter);
}
}

How to bind TextBoxes to an ObservableCollection in an ItemsControl with UniformGrid

If in WPF I have this code:
using System.Collections.ObjectModel;
...
...
public partial class MainWindow : Window
{
public ObservableCollection<String> myMatrixData = new ObservableCollection<String>();
public MainWindow()
{
InitializeComponent();
int i;
for (i = 0; i < 100; i++)
myMatrixData.Add("Test");
myMatrix.ItemsSource = myMatrixData;
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Cell00.Text = myMatrixData[0];
}
}
XAML:
xmlns:sys="clr-namespace:System;assembly=mscorlib"
...
...
<Grid>
<StackPanel>
<ItemsControl x:Name="myMatrix">
<ItemsControl.Resources>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<!-- specify the panel that is the container for the items -->
<ItemsPanelTemplate>
<UniformGrid Rows="10" Columns="10" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- specify the template used to render each item -->
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type sys:String[]}">
<TextBox Text="{Binding Path=., UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Content="Read cell 0/0" Click="Button_Click_1"/>
<TextBlock x:Name="Cell00"/>
</StackPanel>
</Grid>
I just want to reflect the elements in the ObservableCollection myMatrixData to be reflected in a UniformGrid-style ItemsControl named myMatrix. Changing the text in the first cell and pressing the button reveals that this doesn't work.
What am I missing?
ObservableCollection<string> does not update changes to the string because string itself does not implement INotifypropertyChanged
try createing a simple model for your collection
Example:
private ObservableCollection<Matrix> _myMatrix = new ObservableCollection<Matrix>();
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
for (int i = 0; i < 100; i++)
{
MyMatrix.Add(new Matrix { Value = "Test" + i });
}
}
public ObservableCollection<Matrix> MyMatrix
{
get { return _myMatrix; }
set { _myMatrix = value; }
}
.......
public class Matrix : INotifyPropertyChanged
{
private string _value;
public string Value
{
get { return _value; }
set { _value = value; NotifyPropertyChanged("Value"); }
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
Xaml:
<Window x:Class="WpfApplication9.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication9"
Title="MainWindow" Height="350" Width="525" Name="UI">
<StackPanel>
<ItemsControl x:Name="myMatrix" ItemsSource="{Binding MyMatrix}" >
<ItemsControl.ItemsPanel>
<!-- specify the panel that is the container for the items -->
<ItemsPanelTemplate>
<UniformGrid Rows="10" Columns="10" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- specify the template used to render each item -->
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:Matrix}">
<TextBox Text="{Binding Path=Value, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Content="Read cell 0/0" Click="Button_Click_1" />
<TextBlock x:Name="Cell00" />
</StackPanel>
</Window>
I found this pretty useful with making the binding work with NotifyPropertyChanged and also commands
wpf-mvvm-inotifypropertychanged
Implementing the MVVM Pattern
WPF/MVVM Quick Start Tutorial --- Super Handy This One

Error Template Design

It seems like I read another question / answer on this site about this issue but I cannot recall what the answer was and now I cannot find the original post.
I am not a fan of the default error template in WPF. I understand how to change this error template. However, if I add some content to the end of, say, a textbox, the size of the textbox does not change and the added content will (potentially) get clipped. How do I alter the textbox (I believe the correct termonology is adorned element) in this scenario so that nothing gets clipped?
Here is the XAML for the error template:
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder />
<TextBlock Foreground="Red" Text="Error..." />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Here is the XAML for a couple of textboxes in the form:
<StackPanel>
<TextBox Text="{Binding...}" />
<TextBox />
</StackPanel>
Here's a solution adapted from Josh Smith's article on Binding to (Validation.Errors)[0] without Creating Debug Spew.
The trick is to define a DataTemplate to render the ValidationError object and then use a ContentPresenterto display the error message. If there is no error, then the ContentPresenter will not be displayed.
Below, I have shared the code of the sample app that I created.
Here is the XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
SizeToContent="WidthAndHeight"
Title="MainWindow">
<StackPanel Margin="5">
<StackPanel.Resources>
<DataTemplate DataType="{x:Type ValidationError}">
<TextBlock Text="{Binding ErrorContent}" Foreground="White" Background="Red" VerticalAlignment="Center" FontWeight="Bold"/>
</DataTemplate>
</StackPanel.Resources>
<TextBox Name="TextBox1" Text="{Binding Text1, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"/>
<ContentPresenter Content="{Binding ElementName= TextBox1, Path=(Validation.Errors).CurrentItem}" HorizontalAlignment="Left"/>
<TextBox Name="TextBox2" Text="{Binding Text2, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"/>
<ContentPresenter Content="{Binding ElementName= TextBox2, Path=(Validation.Errors).CurrentItem}" HorizontalAlignment="Left"/>
<Button Content="Validate" Click="Button_Click"/>
</StackPanel>
</Window>
The code behind file:
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ViewModel _ViewModel = null;
public MainWindow()
{
InitializeComponent();
_ViewModel = new ViewModel();
DataContext = _ViewModel;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_ViewModel.Validate = true;
_ViewModel.OnPropertyChanged("Text1");
_ViewModel.OnPropertyChanged("Text2");
}
}
}
The ViewModel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace WpfApplication1
{
public class ViewModel : INotifyPropertyChanged, IDataErrorInfo
{
private string _Text1;
public string Text1
{
get { return _Text1; }
set
{
_Text1 = value;
OnPropertyChanged("Text1");
}
}
private string _Text2;
public string Text2
{
get { return _Text2; }
set
{
_Text2 = value;
OnPropertyChanged("Text2");
}
}
public bool Validate { get; set; }
#region INotifyPropertyChanged Implemenation
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region IDataErrorInfo Implementation
public string Error
{
get { return null; }
}
public string this[string columnName]
{
get
{
string errorMessage = string.Empty;
if (Validate)
{
switch (columnName)
{
case "Text1":
if (Text1 == null)
errorMessage = "Text1 is mandatory.";
else if (Text1.Trim() == string.Empty)
errorMessage = "Text1 is not valid.";
break;
case "Text2":
if (Text2 == null)
errorMessage = "Text2 is mandatory.";
else if (Text2.Trim() == string.Empty)
errorMessage = "Text2 is not valid.";
break;
}
}
return errorMessage;
}
}
#endregion
}
}

SelectedItem of ListBox with DataTemplate

I have a ListBox:
<ListBox Name="lbsfHolder"
ItemsSource="{Binding UISupportingFunctions}"
SelectedItem="{Binding Path=SelectedSupportedFunction, Mode=TwoWay}"
SelectionMode="Multiple"
IsSynchronizedWithCurrentItem="True"
HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<controls:SupportingFunction GotFocus="SupportingFunction_GotFocus"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In the ViewModel I have:
private SupportingFunction _selectedSupportedFunction;
public SupportingFunction SelectedSupportedFunction
{
get { return _selectedSupportedFunction; }
set
{
_selectedSupportedFunction = value;
NotifyPropertyChanged("SelectedSupportedFunction");
}
}
But when I'm trying to select any item in list box nothing happens. The SelectedItem is null for the ListBox and for SelectedValue, too. Do I need to add some special code to make this work?
UPD:
I've changed views a bit, now I have:
<UserControl x:Class="RFM.UI.WPF.Controls.SupportingFunction">
<Grid>
<ListBox Name="supportingFunctions"
ItemsSource="{Binding UISupportingFunctions}"
SelectedItem="{Binding Path=SelectedSupportedFunction, Mode=TwoWay}"
SelectionMode="Multiple"
IsSynchronizedWithCurrentItem="True"
HorizontalContentAlignment="Stretch">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30" />
<ColumnDefinition />
<ColumnDefinition Width="30" />
</Grid.ColumnDefinitions>
<TextBox Name="tbsfName" Grid.Column="0" Text="{Binding Path=Title, Mode=TwoWay}"></TextBox>
<TextBox Name="tbsfExperssion" Grid.Column="1" Text="{Binding Path=Expression}" HorizontalAlignment="Stretch"></TextBox>
<Button Name="bsfDel" Grid.Column="2">Del</Button>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Grid>
</UserControl>
In Page where this control placed:
<StackPanel Name="spSupportingFunctions">
<StackPanel Name="spsfOperations" Orientation="Horizontal">
<Button Name="bsfAdd" Width="30" Command="commands:CustomCommands.AddSupportingFunction">Add</Button>
</StackPanel>
<controls:SupportingFunction DataContext="{Binding Self}" />
</StackPanel>
at code behind of this Page
public PlotDataPage()
{
DataContext = new PlotDataViewModel();
InitializeComponent();
}
and this is the full listing of PlotDataViewModel
public class UISupportingFunction : ISupportingFunction
{
public string Title { get; set; }
public string Expression { get; set; }
}
public class PlotDataViewModel : INotifyPropertyChanged
{
public PlotDataViewModel Self
{
get
{
return this;
}
}
private ObservableCollection<UISupportingFunction> _supportingFunctions;
public ObservableCollection<UISupportingFunction> UISupportingFunctions
{
get
{
return _supportingFunctions;
}
set
{
_supportingFunctions = value;
NotifyPropertyChanged("UISupportingFunctions");
}
}
private UISupportingFunction _selectedSupportedFunction;
public UISupportingFunction SelectedSupportedFunction
{
get
{
return _selectedSupportedFunction;
}
set
{
_selectedSupportedFunction = value;
NotifyPropertyChanged("SelectedSupportedFunction");
}
}
public PlotDataViewModel()
{
UISupportingFunctions = new ObservableCollection<UISupportingFunction>();
}
public void CreateNewSupportingFunction()
{
UISupportingFunctions.Add(new UISupportingFunction() { Title = Utils.GetNextFunctionName() });
}
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
public event PropertyChangedEventHandler PropertyChanged;
}
I'm just calling the CreateNewSupportingFunction() method when I click Add button. Everything looks fine - the items is add and I see them. But when I'm clicking on one of the TextBoxes and then to the bsfDel button right to each item I'm getting null in SelectedSupportedFunction.
Maybe it is because of focus event have been handling by TextBox and not by ListBox?
It's either your ItemsSource UISupportingFunctions is not a SupportingFunction object or you did not set the View's Datacontext to your ViewModel.
ViewModel.xaml.cs
this.DataContext = new ViewModelClass();

Strange Behaviour WPF TreeView ItemContainerStyle and ItemTemplate

I just noticed some strange behaviour of WPF's TreeView. I added both ItemContainerStyle to bind to "IsSelected" of my ViewModel and an ItemsTemplated for custom display of my data. But now the user cannot change the selected node anymore. For testing purposes I created a similar UI using ListView and Expander. This version works as excepted. Any tips why TreeView does fail?
<TreeView ItemsSource="{Binding ElementName=frame, Path=list}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}" >
<Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}"/>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate>
<TreeViewItem Header="{Binding}">
<TextBlock Text="{Binding Path= Item.SomeData}"/>
</TreeViewItem>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
EDIT: My data are not hierachical. I just want to get the "collapse" feature on displaying a list. Item.SomeData is not a list. Display of data is as desired. Only selection by mouse fails!
alt text http://img682.imageshack.us/img682/3702/bildy.png
TreeViews work differently. The Items inside a HierarchicalDataTemplate are TreeViewItems and any control you specify inside the HierarchicalDataTemplate will function as its Header. So, basically you are specifying that the Items in your TreeView are TreeViewItems with TreeViewItems as their headers! Instead try this:
<HierarchicalDataTemplate ItemsSource="{Binding Items}">
<Label Content="{Binding}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path= Item.SomeData}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
EDIT: I could not reproduce a DataSource that produces the properties you want to bind to, so I wrote some simple code of my own that shows how it all works. Hopefully you will be able to adapt it to your needs:
<TreeView ItemsSource="{Binding}" Name="Tree">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}" >
<Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}"/>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Items}">
<Label Content="{Binding Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path= SomeData}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
namespace TreeViewSpike
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
List = new List<ItemList>
{
new ItemList
{
Name = "MyList",
Items = new List<Item> {new Item("1"),
new Item("2")}
},
new ItemList
{
Name = "MySecondList",
Items = new List<Item> {new Item("3"),
new Item("4")}
}
};
Tree.DataContext = List;
List[1].IsSelected = true;
}
public List<ItemList> List { get; set; }
}
public class ItemList: INotifyPropertyChanged
{
public string Name{ get; set;}
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
if (PropertyChanged != null)
PropertyChanged(this,
new PropertyChangedEventArgs("IsSelected"));
if(_isSelected)
MessageBox.Show(Name + " selected");
}
}
public List<Item> Items { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
}
public class Item
{
public string SomeData { get; set; }
public Item(string data)
{
SomeData = data;
}
}
}

Resources