wpf bind a controlitem to collection - wpf

hey there
i am having problem i have
List<List<memoryCard>>
that i want to show in my xmal in a button how can I bind my button to the data i want thos is my usercontrol :
<
<ControlTemplate x:Key="ButtonControlTemplate1" TargetType="{x:Type Button}">
<Grid>
<!--i think this is the place where i make mistake :-->
<TextBlock Text="{Binding Path=CardWasfounded}"/>
<Rectangle Margin="4,5,8,2" Stroke="Black" RadiusX="45" RadiusY="45" StrokeThickness="3"/>
</Grid>
</ControlTemplate>
<DataTemplate x:Key="DataTemplate_Level2">
<Button Content="{Binding}" Height="40" Width="50" Margin="4,4,4,4" Template="{DynamicResource ButtonControlTemplate1}"/>
</DataTemplate>
<DataTemplate x:Key="DataTemplate_Level1">
<ItemsControl ItemsSource="{Binding }" ItemTemplate="{DynamicResource DataTemplate_Level2}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
I want every button to have binding to this memory card
class memoryCard : INotifyPropertyChanged
{
#region c'tor
public memoryCard(Brush _buttonColor)
{
buttonColor=_buttonColor;
}
#endregion
#region allReadyFoundedCard
bool cardWasfounded = false;
public bool CardWasfounded
{
get
{
return cardWasfounded;
}
set
{
if (cardWasfounded != value)
{
cardWasfounded = value;
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs("cardWasfounded"));
}
}
}
}
#endregion
#region colorofbutton
string name = "sdasdas";
public Brush buttonColor;
public Brush ButtonColor
{
get
{
return buttonColor;
}
set
{
if (buttonColor != value)
{
buttonColor = value;
if (PropertyChanged != null) PropertyChanged(this,
new PropertyChangedEventArgs("buttonColor"));
}
}
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#endregion
}
whom i want to bind to one of my grids this way :
using this mainwindow class:
public MainWindow()
{
List<List<memoryCard>> lsts = new List<List<memoryCard>>();
for (int i = 0; i < 5; i++)
{
lsts.Add(new List<memoryCard>());
for (int j = 0; j < 5; j++)
{
lsts[i].Add(new memoryCard(Brushes.Green));
}
}
InitializeComponent();
lst.ItemsSource = lsts;
}

Alright, so from what i've gathered you've got a collection of collections of a custom data type which contains a colour that you'd like to bind to.
So heres a small demo that you should (hopefully) be able to expand.
XAML:
<ItemsControl ItemsSource="{Binding Path=MyCollection}"
Height="300" Width="600">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Header="Open Me">
<ItemsControl DataContext="{Binding}" ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button DataContext="{Binding}"
Background="{Binding Path=ButtonColor}"
Content="{Binding Path=CardWasFounded}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And in the code behind:
public ObservableCollection<List<memoryCard>> MyCollection {get; set;}
public MainWindow()
{
DataContext = this;
MyCollection = new ObservableCollection<List<memoryCard>>();
for (int i = 0; i < 5; i++)
{
List<memoryCard> list = new List<memoryCard>();
for (int j = 0; j < 5; j++)
{
list.Add(new memoryCard(Brushes.Green));
}
MyCollection.Add(list);
}
InitializeComponent();
}
Is this similar to what you're trying to do?

When you raise the PropertyChanged event your parameter name (the string you pass in event arguments) must match exactly to the property you bind to. This involves case sensitivity.
You do new PropertyChangedEventArgs("buttonColor") when the property is actually ButtonColor. This will make the WPF binding system ignore the event (since it determines it doesn't have a matching binding so it doesn't have to do anything). You have the same problem with CardWasFounded

Related

WPF Changing Datacontexts and views in same window

I am new to WPF am and porting an application from VC++ 6.0/MFC to c#/WPF (VS2013). Most of my windows development has been in VC++/MFC. I am trying to stick to the MVVM pattern and am writing a few proof of concept apps to get my feet wet. I am having one sticking point so far.
When my app starts up it will present a tree view of customers and bills. I have that working well using a simple hierarchical data template with each level binding to my local data type (view model). What I want to have happen is when a bill is selected (right now I have a button to press on the bill template) I want the treeview to be replaced by a detail view of the bill (I don't want a dialog to pop up).
The Xaml for this is:
<DockPanel>
<TreeView x:Name="trvGroups" ItemsSource="{Binding LBGroups}" VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling">
<TreeView.ItemContainerStyle>
<!--
This Style binds a TreeViewItem to a LBtreeViewItemViewModel
-->
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight" Value="Normal" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate
DataType="{x:Type local:GroupViewModel}"
ItemsSource="{Binding Children}"
>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding GroupName}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate
DataType="{x:Type local:BillViewModel}"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding BillName}" />
<Button Command="{Binding Path=BillEditCommand}">Edit</Button>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</DockPanel>
Right now I have more questions than anything. Should I define each view as user controls and put them in window.resources? Do I use data templates? I assume I would change the data context to point to the detail bill view model. What is the best way to do this?
My goal, to adhere to MVVM as I understand it, is to have nothing in the code behind (or as little as possible).
I'm looking more for pointers to get me started along the right path as I research. I getting a little befuddled at the moment.
Thanks in advance.
I'll Show you a plain Master Details Scenario where you can choose models in your TreeView and Edit Them.
CS :
public partial class MainWindow : Window , INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private ICommand onEditBillCommand;
public ICommand OnEditBillCommand
{
get
{
if (onEditBillCommand == null)
onEditBillCommand = new RelayCommand<Bill>
(
bill => { CurrentBill = bill; }
);
return onEditBillCommand;
}
}
private Bill currectBill;
public Bill CurrentBill
{
get { return currectBill; }
set
{
currectBill = value;
PropertyChanged(this, new PropertyChangedEventArgs("CurrentBill"));
}
}
public List<Customer> Customers
{
get
{
List<Customer> customers = new List<Customer>();
for (int i = 0; i < 5; i++)
{
customers.Add(CreateMockCustomer(i));
}
return customers;
}
}
private Customer CreateMockCustomer(int g )
{
Customer c = new Customer();
c.Name = "John (" + g + ")" ;
for (int i = 0; i < 3; i++)
{
c.Bills.Add(CreateMockBill());
}
return c;
}
private Bill CreateMockBill()
{
Bill b = new Bill();
b.Price = 55.5;
b.BoughtOnDate = DateTime.Now.Date;
return b;
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
public class Customer : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private ObservableCollection<Bill> bills;
public ObservableCollection<Bill> Bills
{
get
{
if (bills == null)
{
bills = new ObservableCollection<Bill>();
}
return bills;
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
public class Bill : INotifyPropertyChanged
{
private double price;
public double Price
{
get { return price; }
set
{
price = value;
PropertyChanged(this, new PropertyChangedEventArgs("Price"));
}
}
private DateTime boughtOnDate;
public DateTime BoughtOnDate
{
get { return boughtOnDate; }
set
{
boughtOnDate = value;
PropertyChanged(this, new PropertyChangedEventArgs("BoughtOnDate"));
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
public interface IRelayCommand : ICommand
{
void RaiseCanExecuteChanged();
}
public class RelayCommand<T> : IRelayCommand
{
private Predicate<T> _canExecute;
private Action<T> _execute;
public RelayCommand(Action<T> execute, Predicate<T> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
private void Execute(T parameter)
{
_execute(parameter);
}
private bool CanExecute(T parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public bool CanExecute(object parameter)
{
return parameter == null ? false : CanExecute((T)parameter);
}
public void Execute(object parameter)
{
_execute((T)parameter);
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
var temp = Volatile.Read(ref CanExecuteChanged);
if (temp != null)
temp(this, new EventArgs());
}
}
XAML :
<Window>
<Window.Resources>
<HierarchicalDataTemplate x:Key="customerTemplate" DataType="{x:Type local:Customer}" ItemsSource="{Binding Bills}">
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Price}" />
<TextBlock Text="{Binding BoughtOnDate}" Grid.Column="1" />
<Button Content="Edit" Grid.Column="2"
Command="{Binding RelativeSource={RelativeSource AncestorType=Window},Path=DataContext.OnEditBillCommand}"
CommandParameter="{Binding}"/>
</Grid>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
<TextBlock Text="{Binding Name}" FontFamily="Arial" FontSize="16" FontWeight="Bold" />
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="0.05*"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TreeView ItemsSource="{Binding Customers}" ItemTemplate="{StaticResource customerTemplate}">
</TreeView>
<Grid Grid.Column="2" DataContext="{Binding CurrentBill, Mode=OneWay}" Background="AliceBlue">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox Text="{Binding Price, Mode=TwoWay}" Margin="50"/>
<TextBox Text="{Binding BoughtOnDate, Mode=TwoWay}" Grid.Row="1" Margin="50"/>
</Grid>
</Grid>

Silverlight 5 ListBox IsSelected style binding broken?

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>

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

How to tie a some controls on one view model

I have a view model with some fields, i.e.
type ViewModel =
member x.a = [1;2;3]
member x.b = [4;5;6]
member x.c = [7]
and in WPF application places some views, as:
<Control.Resources>
<DataTemplate x:Key="ItemTempl">
<TextBlock Text="{Binding}" />
</DataTemplate>
<DataTemplate x:Key="SomeTempl">
<ListBox ItemsSource="{Binding}"
ItemTemplate="{StaticResource ItemTempl}" />
</DataTemplate>
</Control.Resources>
<StackPanel>
<TabControl x:Name="ListBoxViewPresenter">
<TabItem Header="vm.a" Content="{Binding vm.a}"
ContentTemplate="{StaticResource SomeTempl}"/>
<TabItem Header="vm.b" Content="{Binding vm.b}"
ContentTemplate="{StaticResource SomeTempl}"/>
<TabItem Header="vm.c" Content="{Binding vm.c}"
ContentTemplate="{StaticResource SomeTempl}"/>
</TabControl>
<ListBox x:Name="ListBoxViewPresenter">
<ListBoxItem Content="{Binding vm.a}"
ContentTemplate="{StaticResource SomeTempl}" />
<ListBoxItem Content="{Binding vm.b}"
ContentTemplate="{StaticResource SomeTempl}" />
<ListBoxItem Content="{Binding vm.c}"
ContentTemplate="{StaticResource SomeTempl}" />
</ListBox>
</StackPanel>
What should I do to achieve such behavior:
when you clicked on some element in vm.a/b/c in ListBoxViewPresenter so same element in ListBoxViewPresenter is must selected in corresponding TabItem.
UPD:
Specifically my real problem, changing from origin topic.
I have ViewModel with fields: onelines, twolines... and a field with name selected_scheme.
In xaml:
<TreeViewItem Header="{Binding Path=name}" x:Name="ProjectArea">
<TreeViewItem Header="Однониточные планы" Style="{StaticResource MyTreeViewItem}">
<ContentPresenter Content="{Binding onelines}" ContentTemplate="{StaticResource SchemeWrapperTemplate}" />
</TreeViewItem>
<TreeViewItem Header="Двухниточные планы" Style="{StaticResource MyTreeViewItem}">
<ContentPresenter Content="{Binding twolines}" ContentTemplate="{StaticResource SchemeWrapperTemplate}" />
</TreeViewItem>
And data template:
<DataTemplate x:Key="SchemeWrapperTemplate">
<ListBox ItemsSource="{Binding schemes}"
ItemTemplate="{StaticResource SchemeTemplate}"
SelectedItem="{Binding selected_scheme}">
<ListBox.Style>
In other place of the program:
<Grid Grid.Row="1">
<TextBlock Text="{Binding selected_scheme.path}" />
</Grid>
And when you click on some ListBoxes then selected item not changing if you click on a yet SelectedItems.
First of all you should define the ItemsSource of your TabItem and ListBox in the ViewModel they are bound too.
Then you can bind their SelectedItem to a property in your ViewModel.
Here is a code sample (I was too lazy to create a seperate ViewModel class, hence it is mixed with my main window class, but you get the idea...) :
Codebehind :
namespace WpfApplication13
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private object _currentlySelectedItem;
public object CurrentlySelectedItem
{
get { return _currentlySelectedItem; }
set
{
_currentlySelectedItem = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("CurrentlySelectedItem"));
}
}
}
public class MyClass
{
public string MyString { get; set; }
public MyClass(string myString)
{
this.MyString = myString;
}
}
private List<MyClass> _myItemsSource = new List<MyClass>
{
new MyClass("toto"),
new MyClass("tata")
};
public List<MyClass> MyItemsSource
{
get { return _myItemsSource; }
set { _myItemsSource = value; }
}
public object A
{
get { return "toto"; }
}
public object B
{
get { return "tata"; }
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
}
}
xaml :
<Window x:Class="WpfApplication13.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>
<StackPanel>
<TabControl x:Name="ListBoxViewPresenter"
SelectedItem="{Binding CurrentlySelectedItem}"
ItemsSource="{Binding MyItemsSource}" />
<ListBox x:Name="ListBoxViewPresenter2"
SelectedItem="{Binding CurrentlySelectedItem}"
ItemsSource="{Binding MyItemsSource}" />
</StackPanel>
</Grid>
</Window>
New answer after you edited :
It does not make much sense to bind the same object to the SelectedItem property of two different lists that contain different elements. You have to redesign your application or you may be confronted to many problems due to this strange design in the futur.
Still, you can achieve want you want to do with a little codebehing. When the user clicks on one of your ListBox, you set the selected item of your other listbox to null. Then you will be notified when you re-click on the first listbox since the selection goes from null to something.
xaml:
<Window x:Class="WpfApplication13.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>
<StackPanel>
<ListBox x:Name="ListBoxViewPresenter"
SelectedItem="{Binding CurrentlySelectedItem}"
ItemsSource="{Binding MyItemsSource}" />
<ListBox x:Name="ListBoxViewPresenter2"
SelectedItem="{Binding CurrentlySelectedItem}"
ItemsSource="{Binding MyItemsSource2}" />
</StackPanel>
</Grid>
</Window>
codebehind:
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
using System.ComponentModel;
namespace WpfApplication13
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
private object _currentlySelectedItem;
public object CurrentlySelectedItem
{
get { return _currentlySelectedItem; }
set
{
_currentlySelectedItem = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("CurrentlySelectedItem"));
}
}
}
private List<int> _myItemsSource = new List<int> { 1, 2 };
private List<int> _myItemsSource2 = new List<int> { 3, 4 };
public List<int> MyItemsSource
{
get { return _myItemsSource; }
set { _myItemsSource = value; }
}
public List<int> MyItemsSource2
{
get { return _myItemsSource2; }
set { _myItemsSource2 = value; }
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
ListBoxViewPresenter.PreviewMouseDown += ListBoxViewPresenter_PreviewMouseDown;
ListBoxViewPresenter2.PreviewMouseDown += ListBoxViewPresenter2_PreviewMouseDown;
}
void ListBoxViewPresenter_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
ListBoxViewPresenter2.SelectedItem = null;
}
void ListBoxViewPresenter2_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
ListBoxViewPresenter.SelectedItem = null;
}
}
}

ItemsControl modify Items at runtime

in my Silverlight project I have the following ItemsControl :
<ItemsControl x:Name="ItemsList">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border x:Name="brdItem" Opacity="1" MouseLeftButtonDown="brdItem_MouseLeftButtonDown">
<TextBlock x:Name="txtUsername" Text="{Binding Username}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
What I want is :
When my user clicks on an item. I want all the other items Opacity set to "0.3".
When the mouse is released I want all the items to come back to their original state (Opacity="1").
It's very simple if to use the MVVM pattern. Add the Opacity property to the item class and bind it to the Border.Opacity property:
<Border x:Name="brdItem" Opacity="{Binding Opacity}" MouseLeftButtonDown="brdItem_MouseLeftButtonDown">
<TextBlock x:Name="txtUsername" Text="{Binding Username}" />
</Border>
The item class:
public class ItemViewModel : INotifyPropertyChanged
{
public string Username { get; set; }
private double _opacity;
public double Opacity
{
get { return _opacity; }
set
{
_opacity = value;
RaisePropertyChanged("Opacity");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
After that your mouse event will look so:
public void brdItem_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var vm = ((FrameworkElement)sender).DataContext as ItemViewModel;
if (vm != null)
{
vm.Opacity = 1;
this.ItemsList.ItemsSource.OfType<ItemViewModel>()
.Where(item => item != vm)
.ToList()
.ForEach(item => item.Opacity = 0.3);
}
}
To return back to the initial state use the following code:
this.ItemsList.ItemsSource.OfType<ItemViewModel>()
.ToList().ForEach(item => item.Opacity = 1);

Resources