Multiple ListBoxes SelectedItem Binding to same property - c#

I have two listboxes that both contain the same type of items and I have both their SelectedItem Property bound to the same Property on my ViewModel. I expected that, when I selected some item in List A, the Selection in List B disappears. But that is not the case. I need an ugly workaround to make it work how I expect it. Any hints how to make it work without this workaround?
XAML
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding Items1}" SelectedItem="{Binding SelectedItem}" ></ListBox>
<ListBox Grid.Column="1" ItemsSource="{Binding Items2}" SelectedItem="{Binding SelectedItem}" ></ListBox>
<Button Grid.Row="1" Content="NULL" Grid.ColumnSpan="2" Click="ButtonBase_OnClick"></Button>
</Grid>
</Window>
C#
public partial class MainWindow : INotifyPropertyChanged
{
private Item _selectedItem;
public ObservableCollection<Item> Items1 { get; private set; }
public ObservableCollection<Item> Items2 { get; private set; }
public Item SelectedItem
{
get { return _selectedItem; }
set
{
// Uncomment this to make it work
//_selectedItem = null;
//OnPropertyChanged();
_selectedItem = value;
OnPropertyChanged();
}
}
public MainWindow()
{
Items1 = new ObservableCollection<Item>();
Items2 = new ObservableCollection<Item>();
Items1.Add(new Item("A1"));
Items1.Add(new Item("A2"));
Items2.Add(new Item("B1"));
Items2.Add(new Item("B2"));
InitializeComponent();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
SelectedItem = null;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Item
{
public Item(string name)
{
Name = name;
}
string Name { get; set; }
}

Why not binding them (TwoWay binding) to two different Properties (e.g. SelectedItem1 and SelectedItem2), Then:
public Item SelectedItem1
{
get { return _selectedItem1; }
set
{
_selectedItem1 = value;
OnPropertyChanged();
if(value!=null) //Here is the trick.
SelectedItem2=null;
OnPropertyChanged("SelectedItem");
}
}
EDIT: Then for the rest of your application code you can have a SelectedItem property like below. Also don't forget to add OnPropertyChanged("SelectedItem") in setter of both SelectedItem1 and SelectedItem2
public Item SelectedItem
{
get{return SelectedItem1 != null ? SelectedItem1 : SelectedItem2}
}

If it's an ugly workaround you want then tap the SelectionChanged event:
<ListBox x:Name="ListBoxA" ItemsSource="{Binding Items1}" SelectedItem="{Binding SelectedItem}" SelectionChanged="ListBox_SelectionChanged"></ListBox>
<ListBox x:Name="ListBoxB" Grid.Column="1" ItemsSource="{Binding Items2}" SelectedItem="{Binding SelectedItem}" SelectionChanged="ListBox_SelectionChanged" ></ListBox>
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (sender == this.ListBoxA)
this.ListBoxB.UnselectAll();
else
this.ListBoxA.UnselectAll();
}
Better yet, use an attached behaviour to do the same thing.

Related

WPF Sorting an ObservableCollection de-selects a ComboBox

I have a ComboBox where a user can select what JobType they are working on. The ComboBox has a list of AllJobTypes. The problem stems from when a user adds a new JobType, I add the JobType to the AllJobTypes ObservableCollection, then sort it. When the sorting happens the ComboBox get's de-selected and not really sure why. The JobConfig.SelectedJobType.Name never changes in this process. Is there a way to sort an observable collection where it doesn't break the ComboBox?
public class JobTypeList : ObservableCollection<JobType> {
public void SortJobTypes() {
var sortableList = new List<JobType>(this);
sortableList.Sort((x, y) => x.Name.CompareTo(y.Name));
//this works but it creates a bug in the select for JobTypes
for (int i = 0; i < sortableList.Count; i++) {
this.Move(this.IndexOf(sortableList[i]), i);
}
}
And in the XAML
<ComboBox Grid.Column="0" SelectionChanged="JobTypeComboBox_SelectionChanged"
Name="JobTypeComboBox"
ItemsSource="{Binding Path=AllJobTypes}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
SelectedValue="{Binding
Path=JobConfig.SelectedJobType.Name}" />
Instead of sorting the collection in the view model, you should bind the ComboBox's ItemsSource to a CollectionViewSource, where you can specify a SortDescription:
<Window ...
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
...>
<Window.Resources>
<CollectionViewSource x:Key="cvs" Source="{Binding AllJobTypes}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="Name"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</Window.Resources>
...
<ComboBox ItemsSource="{Binding Source={StaticResource cvs}}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
SelectedValue="{Binding JobConfig.SelectedJobType.Name}"/>
...
</Window>
For further information see How to: Sort and Group Data Using a View in XAML
Here's a version using ItemsSource/SelectedItem. Note that you can add a new item to the list and sort it without losing the currently selected item in the view.
The window
<Window
x:Class="SortingAList.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:SortingAList"
mc:Ignorable="d"
Title="MainWindow"
Height="350"
Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox
Text="{Binding NewJobType, Delay=1000}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="200" />
<ComboBox
Grid.Row="1"
ItemsSource="{Binding JobTypes}"
SelectedItem="{Binding SelectedJobType}"
DisplayMemberPath="Name"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="200" />
</Grid>
</Window>
The code
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
public class Notifier : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void Notify([CallerMemberName]string property = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
public class ViewModel : Notifier
{
private JobType _selectedJobType;
private string _newJobType;
public JobTypeList JobTypes { get; private set; }
public JobType SelectedJobType { get => _selectedJobType; set { _selectedJobType = value; Notify(); } }
public string NewJobType { get => _newJobType; set { _newJobType = value; Notify(); AddNewJobType(value); } }
public ViewModel()
{
JobTypes = new JobTypeList();
JobTypes.Add(new JobType { Name = "Butcher" });
JobTypes.Add(new JobType { Name = "Baker" });
JobTypes.Add(new JobType { Name = "LED maker" });
}
private void AddNewJobType(string name)
{
if(JobTypes.Any(x => x.Name == name)) return;
JobTypes.Add(new JobType { Name = name });
JobTypes.SortJobTypes();
}
}
public class JobType : Notifier
{
private string _name;
public string Name { get => _name; set { _name = value; Notify(); } }
}
Using your JobTypesList
public class JobTypeList : ObservableCollection<JobType>
{
public void SortJobTypes()
{
var sortableList = new List<JobType>(this);
sortableList.Sort((x, y) => x.Name.CompareTo(y.Name));
//this works but it creates a bug in the select for JobTypes
for(int i = 0; i < sortableList.Count; i++)
{
this.Move(this.IndexOf(sortableList[i]), i);
}
}
}

How to populate a list of custom user controls with the contents of a bound property

Say I have the following on my viewmodel:
Public ObservableCollection<SubItem> SubItems
Now, each SubItem has the following properties:
Public String Part1
Public String Part2
Given this, I want a ListBox which displays Part1 and Part2 in editable text boxes, one ontop of the other, for each SubItem in SubItems.
To do this, I have created a SubItemUserControl, which is currently just those two text boxes:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<TextBox/>
<TextBox Grid.Row="1"/>
</Grid>
I have then set the ListBox on the MainWindow control as follows:
<ListBox ItemsSource="{Binding Path=SubItems}" Grid.Row="2">
<ListBox.ItemTemplate>
<DataTemplate>
<local:SubItemUserControl/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This displays two empty text boxes for each SubItem.
What I don't know, is how to bind the two properties in the SubItem to the two text-boxes in the SubItemUserControl.
1.Create SubItemModel which implement INotifyPropertyChange Interface:
public class SubItem : BindableBase
{
private string _part1;
public string Part1
{
get
{
return _part1;
}
set
{
_part1 = value;
OnPropertyChanged("Part1");
}
}
private string _part2;
public string Part2
{
get
{
return _part2;
}
set
{
_part2 = value;
OnPropertyChanged("Part2");
}
}
}
2.Initialize your ObservableCollection collection in yout ViewModel:
private ObservableCollection <SubItem> _subItems;
public ObservableCollection<SubItem> SubItems
{
get
{
return _subItems;
}
set
{
_subItems = value;
OnPropertyChanged("SubItems ");
}
}
public MainViewModel()
{
_subItems = new ObservableCollection<SubItem>
{
new SubItem{Part1 = "AP1", Part2 = "AP2"},
new SubItem{Part1 = "BP1", Part2 = "BP2"},
new SubItem{Part1 = "CP1", Part2 = "CP2"},
};
}
3.Set your View when Part1 is on top of Part2 and bind your properties:
<ListBox ItemsSource="{Binding Path=SubItems}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" x:Name="Part1" Text="{Binding Part1}"/>
<TextBox Grid.Row="1" x:Name="Part2" Text="{Binding Part2}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
4.Implement your BindableBase:
public class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}

WPF ListBox Not updating

I searched in this forum but I was unable to find a solution for my specific scenario.
I`m trying to understand WPF and MVVM and I build a simple WPF for this.
My Data Model is (I Implemented INotifyPropertyChanged here and the constructor initializes all properties):
namespace MyApp.ui.Models
{
public class Server : INotifyPropertyChanged
{
private int id;
public int ID
{
get { return id; }
set { id = value; }
}
private string name;
public string Name
{
get { return name; }
set { name = value; OnPropertyChanged(Name); }
}
private string ipAddress;
public string IPAddress
{
get { return ipAddress; }
set { ipAddress = value; OnPropertyChanged(IPAddress); }
}
public Server(int ServerID, string ServerName, string ServerIpAddress)
{
ID = ServerID;
Name = ServerName;
IPAddress = ServerIpAddress;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if(handler != null)
{
handler(this, new PropertyChangedEventArgs( propertyName ) );
}
}
}
}
My ViewModel (used by WPF Code Behind):
namespace MyApp.ui.ViewModels
{
public class ServersViewModel
{
private ObservableCollection<Server> server;
public ObservableCollection<Server> Servers
{
get { return server; }
set { server = value; }
}
public ServersViewModel()
{
Servers = new ObservableCollection<Server>
{
new Server(001, "Server001", #"192.168.254.3"),
new Server(002, "Server002", #"100.92.0.200"),
new Server(003, "Server003", #"64.32.0.3"),
new Server(004, "Server004", #"172.10.0.4"),
new Server(005, "Server005", #"165.23.0.233"),
new Server(006, "Server006", #"81.22.22.6"),
new Server(007, "Server007", #"10.10.0.7")
};
}
public void ChangeServerNames()
{
//Before Change
foreach (var item in Servers)
{
MessageBox.Show(item.Name);
}
int count = 1000;
foreach (var item in Servers)
{
item.Name = "Server" + count.ToString();
count += 1000;
}
//After Change
foreach (var item in Servers)
{
MessageBox.Show(item.Name);
}
}
}
}
My WPF Main View (Main Menu) loads a Custom user control (ExplorerView) with the following XAML code (Contains a listbox and each listbox item contains 1 checkbox + image + textblock)
<UserControl x:Class="MyApp.ui.Views.ExplorerView"
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:MyApp.ui.Views"
mc:Ignorable="d"
d:DesignHeight="400" d:DesignWidth="200">
<Grid>
<ListBox ItemsSource="{Binding Servers}" Margin="2">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox VerticalContentAlignment="Center" Margin="4">
<StackPanel Orientation="Horizontal">
<Image Source="/resources/server64.png" Height="30" Margin="4"></Image>
<TextBlock Text="{Binding Name}"
VerticalAlignment="Center" Margin="4"></TextBlock>
</StackPanel>
</CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
Finally the MainView Code Behind loads the ServersViewModel so the ExplorerView Control can Bind the data.
namespace MyApp.ui
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ServersViewModel context { get; set; }
public MainWindow()
{
InitializeComponent();
context = new ServersViewModel();
DataContext = context;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
context.ChangeServerNames();
}
}
}
That said, I have 2 Questions:
1) As you can see, in the MainView I implemented a Button click event that calls into ServersViewModel.ChangeServerNames() Method. The problem is that my TextBlock in ExplorerView Control does not show the updated data.
I ChangeServerNames() I also use a MessageBox to show the Values Before and After the change, and I see that the values are changing, not sure why the ListBox/TextBlock is not updating...!!! (I already tested many other possible solutions, but I can`t get it working...)
2) I read that the CodeBehind in MainView (and all other views) should only contain the InitializeComponent(); and "DataContext = context;" at Maximum...
If that is true, where the Events for button clicks and others should be placed?
Finally the code for the MainWindow XAML:
<Window
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:MyApp.ui"
xmlns:Views="clr-namespace:MyApp.ui.Views"
x:Class="MyApp.ui.MainWindow"
mc:Ignorable="d"
Title="Server" MinHeight="720" MinWidth="1024"
Height ="720" Width="1024">
<Grid Margin="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="Auto"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="41*"/>
<RowDefinition Height="608*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<GridSplitter Grid.Column="1" Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="Stretch"
Background="Gray"
ShowsPreview="True"
Width="4" Margin="0,2,0,4"
/>
<Views:MenuView Grid.ColumnSpan="3"/>
<Views:FooterView Grid.Row="2" Grid.ColumnSpan="3" />
<Views:ExplorerView Grid.Column="0" Grid.Row="1" />
<!--Temp Tests-->
<StackPanel Margin="12" Grid.Column="3" Grid.Row="1" Width="Auto" Height="Auto" Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left">
<Button Margin="4" Width="120" Height="30" Content="Change Data Test..." Click="Button_Click" />
</StackPanel>
</Grid>
</Window>
Thank you for your time...
Ok, I found the problem...
Instead of
set { name = value; OnPropertyChanged(Name); }
set { ipAddress = value; OnPropertyChanged(IPAddress); }
I was missing the Quotesfor the String argument on method call
The correct form is
set { name = value; OnPropertyChanged("Name"); }
set { ipAddress = value; OnPropertyChanged("IPAddress"); }
Weird that the compiler didn`t throw any error.... The Method
private void OnPropertyChanged(string propertyName)
Is "Asking" for a string as input arg.
AnyWay the best to avoid these errors (that I found) is to write the event like this (The caller supplies it`s own Public Name):
private void OnPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Now I can do
set { name = value; OnPropertyChanged(); }
set { ipAddress = value; OnPropertyChanged(); }
Thank you.

Selecting all CheckBoxes in ListBox not displayed properly

If I press a button "Check All" all CheckBoxes in a ListBox should be selected and added to a list where all checked items are stored. The problem is that only the visible checkboxes are updated properly.
Here is my CheckBoxListItem class:
public class Cbli : INotifyPropertyChanged
{
private string _name;
private Boolean _isChecked;
public string Name
{
get { return _name; }
set { _name = value; OnPropertyChanged("Name"); }
}
public bool IsChecked
{
get { return _isChecked; }
set { _isChecked = value; OnPropertyChanged("IsChecked"); }
}
public override string ToString()
{
return string.Format("Name: {0}, IsChecked: {1}", _name, _isChecked);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML:
<Window x:Class="ListBoxBuggy.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:listBoxBuggy="clr-namespace:ListBoxBuggy"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}" WindowStartupLocation="CenterScreen">
<Window.Resources>
<DataTemplate x:Key="CheckBoxListItemTemplateNew" DataType="listBoxBuggy:Cbli">
<CheckBox Name="CheckBox"
IsChecked="{Binding IsChecked}"
Checked="Update"
Unchecked="Update"
FontSize="14">
<TextBlock Text="{Binding Name}" FontSize="14"/>
</CheckBox>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox HorizontalAlignment="Left" Height="300" VerticalAlignment="Top" Width="168"
ItemsSource="{Binding MyItemList}"
ItemTemplate="{StaticResource CheckBoxListItemTemplateNew}"
/>
<ListBox HorizontalAlignment="Left" Height="290" Margin="297,10,0,0" VerticalAlignment="Top" Width="195"
ItemsSource="{Binding CheckedItems}"
/>
<Button Content="Check All" HorizontalAlignment="Left" Margin="173,10,0,0" VerticalAlignment="Top" Width="75" Click="Check_All"/>
<Button Content="Uncheck All" HorizontalAlignment="Left" Margin="173,52,0,0" VerticalAlignment="Top" Width="75" Click="Uncheck_All"/>
</Grid>
</Window>
And the code behind:
public partial class MainWindow : Window
{
public ObservableCollection<Cbli> MyItemList { get; set; }
public ObservableCollection<Cbli> CheckedItems { get; set; }
public MainWindow()
{
// add dummy data
MyItemList = new ObservableCollection<Cbli>();
CheckedItems = new ObservableCollection<Cbli>();
for (int i = 0; i < 20; i++)
{
Cbli cbli = new Cbli
{
Name = "Test " + i,
IsChecked = i < 5 || i > 15
};
MyItemList.Add(cbli);
if (cbli.IsChecked)
CheckedItems.Add(cbli);
}
InitializeComponent();
}
private void Update(object sender, RoutedEventArgs e)
{
CheckBox selectedCheckbox = (CheckBox)sender;
Cbli cbli = (Cbli)selectedCheckbox.DataContext;
if (cbli.IsChecked)
CheckedItems.Add(cbli);
else
CheckedItems.Remove(cbli);
}
private void Check_All(object sender, RoutedEventArgs e)
{
foreach (Cbli cbli in MyItemList)
cbli.IsChecked = true;
}
private void Uncheck_All(object sender, RoutedEventArgs e)
{
foreach (Cbli cbli in MyItemList)
cbli.IsChecked = false;
}
}
After scrolling down, so all 20 items on the left list are visible and clicking then the "check all" button is working pretty well, but I don't know why.
Can someone please tell me what is wrong with that implementation? Checking/unchecking a single CheckBox is working, but the Check/Uncheck all buttons aren't working properly.
The comment from Blam (setting VirtualizingStackPanel.VirtualizationMode="Standard" was nearly the solution.
Add VirtualizingStackPanel.IsVirtualizing="False":
<ListBox HorizontalAlignment="Left" Height="300" VerticalAlignment="Top" Width="168"
ItemsSource="{Binding MyItemList}"
ItemTemplate="{StaticResource CheckBoxListItemTemplateNew}"
VirtualizingStackPanel.IsVirtualizing="False" />
This solved the problem (at least for me)

Change item checkbox on ListBox selection change

I have a ListBox created by ItemTemplate and Binding
<controls:PanoramaItem Header="{Binding AppResources.SettingsSubpage2, Source={StaticResource LocalizedStrings}}" HeaderTemplate="{StaticResource HeaderTemplate}">
<Grid>
<ListBox x:Name="DayOfWeekSelector" ItemTemplate="{StaticResource DayOfWeekTemplate}" ItemsSource="{Binding DayOfWeekElementList}" Foreground="{StaticResource AppForegroundColor}" LostFocus="DayOfWeekSelector_LostFocus" HorizontalAlignment="Left" Width="420" />
</Grid>
</controls:PanoramaItem>
Template code:
<phone:PhoneApplicationPage.Resources>
<!--- ... --->
<DataTemplate x:Key="DayOfWeekTemplate">
<Grid Height="65" Width="332">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<CheckBox IsChecked="{Binding IsActive, Mode=TwoWay}" Tag="{Binding}" d:LayoutOverrides="Width, Height" BorderBrush="{StaticResource AppBackgroundColor}" Background="{StaticResource ScheduleBackgroundAccentsColor}" Grid.Column="0" Unchecked="CheckBox_Unchecked" />
<StackPanel Grid.Column="1" Orientation="Horizontal">
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" d:LayoutOverrides="Width"/>
<TextBlock Text="{Binding TaskCounter, Mode=OneWay, Converter={StaticResource DayOfWeekCounter}}" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10,0,0,0"/>
</StackPanel>
</Grid>
</DataTemplate>
<!--- ... --->
And it's working fine. I've got all my items on the list. Checkboxes are binded to appropriate elements (clicking on it is changing proper value).
But by default ListBox can be also selected. Selection high-light TextBox binded to Name but don't change CheckBox (binded to IsActive). How can I tie item selection changing to checkbox state changing (in Silverlight)?
Edit:
public partial class SettingsPage : PhoneApplicationPage, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public List<DayOfWeekElement> DayOfWeekList
{
get
{
return CyberSyncPlanBase.Instance.DayOfWeekElementList;
}
set
{
CyberSyncPlanBase.Instance.DayOfWeekElementList = value;
NotifyPropertyChanged("DayOfWeekList");
}
}
public SettingsPage()
{
InitializeComponent();
DayOfWeekSelector.DataContext = CyberSyncPlanBase.Instance;
}
private void DayOfWeekSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
DayOfWeekElement dowe = (DayOfWeekElement) DayOfWeekSelector.SelectedItem;
if (dowe != null)
dowe.IsActive = (dowe.IsActive) ? false : true;
}
And in singleton INotifyPropertyChanged i've implemented in the same way:
private List<DayOfWeekElement> dayOfWeekElementList;
public List<DayOfWeekElement> DayOfWeekElementList
{
get { return dayOfWeekElementList; }
set
{
dayOfWeekElementList = value;
RecalcWeekTasks();
NotifyPropertyChanged("DayOfWeekElementList");
}
}
Bottom class:
public class DayOfWeekElement
{
public string Name { get { return this.DayOfWeek.ToStringValue(); } }
public bool IsNotEmpty { get { return (TaskCounter > 0); } }
public int TaskCounter { get; set; }
public bool IsActive { get; set; }
public DayOfWeek DayOfWeek { get; set; }
}
I think you could use the SelectedItem property of the ListBox control.
A possible implementation could be this:
Subscribe to the event SelectedIndexChanged of the ListBox.
Get the selected item.
For the selected item, change its IsActive property to true.
This works if the interface INotifyPropertyChanged is implemented in your data class.
E.g.:
public class DayOfWeekElement : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private bool isActive = false;
public bool IsActive {
get
{
return this.isActive;
}
set
{
if (value != this.isActive)
{
this.isActive= value;
NotifyPropertyChanged("IsActive");
}
}
}
}

Categories