WPF Binding with Notifyi does not update UI - c#

My issue is that UI is not updated even when PropertyChanged is fired.
XAML:
<ListBox Name="BookShelf" Visibility="Hidden" SelectedItem="{Binding SelectedItem}" Panel.ZIndex="1" Height="Auto" Grid.Column="3" Margin="8,50,0,0" HorizontalAlignment="Center" ItemsSource="{Binding BookShelf}" Background="Transparent" Foreground="Transparent" BorderThickness="0" BorderBrush="#00000000">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel VerticalAlignment="Center" Orientation="Vertical">
<TextBlock FontSize="14" Margin="0,10,0,0" FontWeight="Bold" Foreground="Black" HorizontalAlignment="Center" Text="{Binding Path=DbId}" />
<TextBlock FontSize="16" FontWeight="Bold" Width="170" TextWrapping="Wrap" Foreground="Black" Margin="5" HorizontalAlignment="Center" Text="{Binding Path=DisplayName}" />
<Image HorizontalAlignment="Center" Source="{Binding Path=bookImage}" Width="200" Height="200" Margin="0,0,0,10" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And:
<ComboBox Margin="8,15,0,0" Name="bookShelf_ComboBox" ItemsSource="{Binding BookShelf}" SelectedItem="{Binding SelectedItem}" VerticalAlignment="Center" HorizontalAlignment="Center" DisplayMemberPath="DisplayName" Height="22" Width="140" Visibility="Visible" SelectionChanged="bookShelf_ComboBox_SelectionChanged"/>
Viewmodel:
public class BookShelfViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public event ShowContentInBrowser ShowDatabaseContent;
public BookShelfViewModel(ShowContentInBrowser showMethod)
{
ShowDatabaseContent += showMethod;
}
private ObservableCollection<DbInfo> _BookShelf = new ObservableCollection<DbInfo>();
public ObservableCollection<DbInfo> BookShelf
{
get
{
if (_BookShelf == null)
_BookShelf = new ObservableCollection<DbInfo>();
return _BookShelf;
}
set
{
if (value != _BookShelf)
_BookShelf = value;
}
}
private DbInfo _selectedItem { get; set; }
public DbInfo SelectedItem
{
get
{
return _selectedItem;
}
set
{
if (_selectedItem != value)
{
_selectedItem = value;
RaisePropertyChanged(new PropertyChangedEventArgs("SelectedItem"));
if (_selectedItem == null)
return;
if (_selectedItem.RelatedId != null)
ShowDatabaseContent(_selectedItem, _selectedItem.RelatedId);
else
ShowDatabaseContent(_selectedItem, _selectedItem.RelatedId);
}
}
}
public void RaisePropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
PropertyChanged(this, e);
}
}
This code I'm using is for setting DataContext and SelectedItem:
await System.Windows.Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background, new Action(
() => this.BookShelf.SelectedItem = dbInfo
)
);
And DataContext:
await System.Windows.Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background, new Action(
() => this.BookShelf.DataContext = bookShelfViewModel
)
);
I'm very new for this MVVM design and as far as I have can tell from articles I have read, I cant find what's wrong. I'm guessing that using Dispatcher is not necessary but I don't think it matters in this case...
ListBox does show my objects but updating SelectedItem is the issue here...
UPDATE:
Heres my code for DbInfo:
public class DbInfo
{
public int RelatedId { get; set; }
public string DbId { get; set; }
public TBase3.Article currentArticle { get; set; }
public string LinkId { get; set; }
public bool IsArticle { get; set; }
public string folder { get; set; }
public bool IsNamedArticle { get; set; }
public int currentBlockIndex { get; set; }
public int currentBlockCount { get; set; }
public string DisplayName { get; set; }
public int VScrollPos { get; set; }
public int THTextVersion { get; set; }
public bool isHtmlToc { get; set; }
public ImageSource bookImage { get; set; }
}
Reminder that when ever I set new value for ViewModel -> SelectedItem and It goes to PropertyChanged(this, e); line. It does not Selected that DbInfo as Selected in ListBox.
Update2:
I got right side of my window a list of books, like a Book Shelf many books in it.
It shows all book with scroll. Book which is selected its content is being shown in window.
But If for reason I want to change to another book from code-behind, it updates it content to webbrowser but not update ListBox that certain book as SelectedItem
ANSWER:
Okay I found the answer right now. Code which set BookShelf.SelectedItem = dbInfo should be bookShelfViewModel.SelectedItem = bookShelfViewModel.BookShelf.First(x => x.DbId == dbInfo.DbIf);

await System.Windows.Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => this.BookShelf.DataContext = bookShelfViewModel));
That does not look good, where do you do this? I would reccomend the use of Galasoft MVVM Light and the usage of a ViewModelLocator, for setting your DataContext(avaliable through nuget).It provides you with a ViewModelBase, with all your propertychanged needs and the works, which you may extend to suit your neeeds. It sounds like the DataContext, may be the actual problem if PropertyChanged isn't even raised.
EDIT:
As pointed out by Clemens the Mode=TwoWay in the binding is not needed here, as it is the default for the SelectedItem property anyway, I'll just leave it as an example....
<ComboBox Margin="8,15,0,0" Name="bookShelf_ComboBox" ItemsSource="{Binding BookShelf}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" VerticalAlignment="Center" HorizontalAlignment="Center" DisplayMemberPath="DisplayName" Height="22" Width="140" Visibility="Visible">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<command:EventToCommand Command="{Binding SelectedItemChangedCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
I noticed your SelectionChanged="bookShelf_ComboBox_SelectionChanged" in the code, this is "nasty", use event to command instead, because you are tying your viewmodel to your views cbox. The code above will execute a command with arguments included, add the following to your viewmodel.
public ICommand SelectedItemChangedCommand{ get; set;}
// somewhere in your code..
SelectedItemChangedCommand = new RelayCommand<SelectedItemChangedCommand>(OnSelectedItemChanged);
protected void OnSelectedItemChanged(SelectedItemChangedCommand e)
{
// Do your magic here! Or why not just call this method from the setter of your bound property?
}
The WPF Cheat Sheet is a nice compact list of all types of bindings, which is very handy, I used to have it both at wall and home, when I was learning WPF :)
Hope it helps
Cheers
Stian

what you mean by the UI is not updated? you set a new ItemsSource and dont see any Changes?
if that is the case change your Property to
public ObservableCollection<DbInfo> BookShelf
{
get
{
if (_BookShelf == null)
_BookShelf = new ObservableCollection<DbInfo>();
return _BookShelf;
}
set
{
if (value != _BookShelf)
{ _BookShelf = value;
RaisePropertyChanged(new PropertyChangedEventArgs("BookShelf"));
}
}
btw i use ObservableCollection in another way. i just initialize it once and use Clear, Add, Remove.
If the ItemsSource is not your Problem pls post the Code for DbInfo, and write something more to your "UI is not updated" problem

Okay I found the answer right now. Code which set BookShelf.SelectedItem = dbInfo should be bookShelfViewModel.SelectedItem = bookShelfViewModel.BookShelf.First(x => x.DbId == dbInfo.DbIf);

Related

WPF XAML can not see checkboxes in ListView

I'm working on a small WPF project,
for now it contains one window which should display as much checkboxes are many values in lists are.
For testing purposes, before I get values from database I tried something like this:
public class StatusOption
{
public string name { get; set; }
public bool IsSelected { get; set; }
}
public void GetSerialNumbers()
{
List<StatusOption> serialNumbers = new List<StatusOption>();
for(int i = 0; i<10;i++)
{
StatusOption x = new StatusOption();
x.name = "Random name" + i;
x.IsSelected = false;
serialNumbers.Add(x);
}
}
And my xaml looks like this:
<ListBox x:Name="SerialNumbersListBox"
AllowDrop="True"
Grid.ColumnSpan="2"
Grid.Row="2"
ItemsSource="{Binding GetSerialNumbers}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding serialNumbers}"
IsChecked="{Binding IsSelected}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
But unfortunatelly nothing is displayed below textbox...
But for now everything is empty, and I can not find out why..
Thanks guys
Cheers
You could not bind a method. Please use property instead.
<ListBox HorizontalAlignment="Left" Height="171" Margin="334,96,0,0" VerticalAlignment="Top" Width="248" AllowDrop="True" x:Name="SerialNumbersListBox"
ItemsSource="{Binding SerialNumbers}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding name}"
IsChecked="{Binding IsSelected}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
public class SerialNumbersListBoxViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public class StatusOption
{
public string name { get; set; }
public bool IsSelected { get; set; }
}
private ObservableCollection<StatusOption> _SerialNumbers;
public ObservableCollection<StatusOption> SerialNumbers
{
get
{
return _SerialNumbers;
}
set
{
if (value != _SerialNumbers)
{
_SerialNumbers = value;
OnPropertyChanged(nameof(SerialNumbers));
}
}
}
public void GetSerialNumbers()
{
if (_SerialNumbers == null)
_SerialNumbers = new ObservableCollection<StatusOption>();
for (int i = 0; i < 10; i++)
{
StatusOption x = new StatusOption();
x.name = "Random name" + i;
x.IsSelected = false;
_SerialNumbers.Add(x);
}
}
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public SerialNumbersListBoxViewModel()
{
GetSerialNumbers();
}
}
You can refer this link for more details
Regard!
You cannot bind to methods, you can only bind to Properties or DependencyProperties.
So you need to create a Property for your serialNumbers. You should also implement INotifyPropertyChanged, so that the ListBox can know when your property changed.
public List<object> SerialNumbers
{
get => this._serialNumbersProperty;
set
{
if (!List<object>.Equals(value, this._serialNumbersProperty))
{
this._serialNumbersProperty = value;
OnPropertyChanged(nameof(this.SerialNumbers));
}
}
}
<ListBox x:Name="SerialNumbersListBox"
AllowDrop="True"
Grid.ColumnSpan="2"
Grid.Row="2"
ItemsSource="{Binding SerialNumbers}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding name}"
IsChecked="{Binding IsSelected}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Changing the ViewModel's property from another ViewModel

I have the ListBox on my MainView.xaml, selecting the Item forces the ContentControl to display different UserControls. I use Caliburn.Micro library in this propgram. Here's some code:
<ListBox Grid.Row="1" Grid.Column="1" x:Name="ItemsListBox" SelectedItem="0" ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding TextBlock1Text}" x:Name="TextBlock1"/>
<ContentControl Grid.Row="3" Grid.Column="1" Content="{Binding ElementName=ItemsListBox, Path=SelectedItem.Content}" />
The MainViewModel.cs:
private string _name;
public string Name
{
get => _name;
set
{
_name = value;
NotifyOfPropertyChange(() => Name);
}
}
private string _textBlock1Text;
public string TextBlock1Text
{
get => _textBlock1Text;
set
{
_textBlock1Text = value;
NotifyOfPropertyChange(() => TextBlock1Text);
}
}
public MainViewModel()
{
TextBlock1Text = "Test";
Items = new ObservableCollection<ItemsModel>()
{
new ItemsModel { Name="Useless", Content=null },
new ItemsModel { Name="TextChangerViewModel", Content=new TextChangerViewModel(TextBlock1Text) }
};
}
public ObservableCollection<ItemsModel> Items { get; set; }
The ItemsModel.cs:
public class ItemsModel
{
public string Name { get; set; }
public object Content { get; set; }
}
And finally the TextChangerViewModel.cs:
public class TextChangerViewModel : Conductor<object>
{
private string _textBlock1Text;
public string TextBlock1Text
{
get => _textBlock1Text;
set
{
_textBlock1Text = value;
NotifyOfPropertyChange(() => TextBlock1Text);
}
}
public TextChangerViewModel(string textBlock1Text) //passing parameter from another ViewModel
{
TextBlock1Text = textBlock1Text;
}
}
So, the main question is how to change the TextBlock1Text (and the Text value of TextBlock in .xaml as well) in the MainViewModel.cs from the TextChangerViewModel.cs? I was thinking about using something like NotifyCollectionChanged on my Items ObservableCollection, but it work with collection of ItemsModel, not with the VM's, so I'm stuck here.
I'm also not sure if having public object Content { get; set; } in ItemsModel.cs is a good thing if I'm targeting the MVVM pattern, but I don't know the other way to do it (I'm very new to MVVM).
UPD
I'm looking for the property-changing way because I need to change the TextBlock1Text Text from another UserControl. Suppose I have the button on my TextChangerView.xaml: <Button Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Content="Change da text" cal:Message.Attach="ChangeTextButton"/>
And after the click on it I want the text on the parental MainView.xaml to change. But the thing is, I don't know how to change properties in this case, as I wrote above why.
Change the the binding of textblox1 to reference the selected item.
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding ElementName=ItemsListBox, Path=SelectedItem.Name}" x:Name="TextBlock1"/>
or
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding ElementName=ItemsListBox, Path=SelectedItem.Content.TextBlock1Text}" x:Name="TextBlock1"/>

WPF MVVM creating multiple datagrids and bind items to them

I have met following problem in WPF binding.
I need to load objects from XML file and create list of loaded items in listbox, and when listbox item is selected then display suitable set of objects.
I can do it in 'code behind' style, but I really want to do it in proper MVVM way.
My Matrixes class is generated by xsd2code from which contains:
List<CorrectionMatrixType> correctionMatrixField;
and follows
public partial class CorrectionMatrixType {
public MatrixType A {get; set;}
public MatrixType B {get; set;}
public MatrixType C {get; set;}
... }
How can I create 'dynamically'something like Grid with three DataGrids by Viewmodel and bind each matrix (A,B,C) to them which content will change depends of value selected in listbox? I know that to bind my MatrixType to DataGrid i have to use ValueConverter to convert my object to two-dimensional array.
Maybe I have to admit I am using MVVM Light.
Please, any suggestions?
I would use the INotifyPropertyChanged Interface. Here is a small example (not exactly your case, but enough to show the principle, I think):
MatrixType class:
public class MatrixType
{
public string Name { get; set; }
public string Width { get; set; }
public string Height { get; set; }
}
Xaml:
<Window.DataContext>
<local:MainViewModel />
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0" ItemsSource="{Binding Items}" DisplayMemberPath="Name" SelectedItem="{Binding SelectedItem}"></ListBox>
<Grid Grid.Column="1">
<StackPanel Orientation="Vertical">
<TextBox Text="{Binding SelectedItem.Name}" Height="30"/>
<TextBox Text="{Binding SelectedItem.Height}" Height="30"/>
<TextBox Text="{Binding SelectedItem.Width}" Height="30"/>
</StackPanel>
</Grid>
</Grid>
MainViewModel.cs:
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
var list = new List<MatrixType>
{
new MatrixType {Height = "233", Name = "A", Width = "133"},
new MatrixType {Height = "333", Name = "B", Width = "233"},
new MatrixType {Height = "433", Name = "C", Width = "333"}
};
Items = new ObservableCollection<MatrixType>(list);
}
private MatrixType _selectedItem;
public MatrixType SelectedItem
{
get => _selectedItem;
set { _selectedItem = value; OnPropertyChanged(); }
}
public ObservableCollection<MatrixType> Items { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
MainViewModel.cs (when using MVVM Light):
public class MainViewModel : ObservableObject
{
public MainViewModel()
{
var list = new List<MatrixType>
{
new MatrixType {Height = "233", Name = "A", Width = "133"},
new MatrixType {Height = "333", Name = "B", Width = "233"},
new MatrixType {Height = "433", Name = "C", Width = "333"}
};
Items = new ObservableCollection<MatrixType>(list);
}
private MatrixType _selectedItem;
public MatrixType SelectedItem
{
get => _selectedItem;
set { _selectedItem = value; RaisePropertyChanged(); }
}
public ObservableCollection<MatrixType> Items { get; set; }
}
I wrote solution by myself, I don't know if it is good MVVM solution.
I re-write my XSD so MatrixType becomes SimpleMatrix, and now:
XAML:
<ListBox Margin="5,20,0,5" ItemsSource="{Binding CorrectionMatrixes}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<command:EventToCommand Command="{Binding SelectionChangedCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
<i:EventTrigger EventName="Loaded">
<command:EventToCommand Command="{Binding ListBoxLoadedCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
<DataGrid CanUserAddRows="False" HeadersVisibility="None" ItemsSource="{Binding CorrectionMatrixA, Converter={StaticResource MatrixToArray }}"/>
And in my viewmodel:
public RelayCommand<SelectionChangedEventArgs> SelectionChangedCommand => new RelayCommand<SelectionChangedEventArgs>(SelectionChanged);
public RelayCommand<RoutedEventArgs> ListBoxLoadedCommand => new RelayCommand<RoutedEventArgs>(ListBoxLoaded);
private string CorrectionMatrixName { get; set; }
private void ListBoxLoaded(RoutedEventArgs obj)
{
if (obj.Source is ListBox listBox)
{
listBox.SelectedIndex = 0;
}
}
private void SelectionChanged(SelectionChangedEventArgs obj)
{
if (obj.AddedItems.Count <= 0) return;
if (obj.AddedItems[0] is CorrectionMatrix matrix)
{
CorrectionMatrixName = matrix.name;
}
RaisePropertyChanged(() => CorrectionMatrixA);
}
public SimpleMatrix CorrectionMatrixA
{
get
{
try
{
var x = Matrixes.Correction.Where(a => a.name == CorrectionMatrixName)
.Select(a => a.A).Single();
return x;
}
catch (InvalidOperationException)
{
return null;
}
}
}
Matrixes are loaded by:
Matrixes = settingsLoader.LoadMatrixes(Properties.Resources.MatrixesSettings);
How does it all works:
when user control is loaded => selected index on listbox is setting to zero
When selected item on listbox is changed it fires event that changes CorrectionMatrixName
Binding properties returns suitable matrix finding it in array by name
I don't post Converter code - it doesn't matter here.
Thats full, my own solution that worked for me. I hope it will helps other people

How to access checkbox in ListBox items

I'm trying to make a ListBox with checkboxes into it.
In xaml I have:
<ListBox ItemsSource="{StaticResource ResourceKey=lstMaterialesCL}" SelectionMode="Multiple" Name="lstMaterial" >
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Name="chkMaterial" Content="{Binding DescCompuesta}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and my ListBox looks as:
It is ok, but look it, when I checked "Municipales" the item in the ListBox is not selected, and when I select in the ListBox "Industriales" it is not checked
If I inspect the items selected into the ListBox it don't coincide with the items Checked
foreach (var item in lstMaterial.SelectedItems)
{
MessageBox.Show(((MaterialesCL)item).DescCompuesta);
}
It shows me "Oficiales", "Industriales" and "Destrucciones" but the user was want select "Municipales" and "Destrucciones"
How I can to make coincide the ListBox items selected with the CheckBox checked if the CheckBox checked is mandatory?
XAML:
DescCompuestaList is a list of CheckListGeneric
<ListBox ItemsSource="{Binding DescCompuestaList}" >
<ListBox.ItemTemplate>
<HierarchicalDataTemplate>
<CheckBox Content="{Binding DescCompuesta}" IsChecked="{Binding IsChecked}"/>
</HierarchicalDataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Here your CheckListGeneric class
public class CheckListGeneric: ViewModelBase
{
#region ..:: Fields ::..
private bool _isChecked;
#endregion
#region ..:: Properties ::..
public long Id { get; set; }
public string DescCompuesta{ get; set; }
public bool IsChecked
{
get { return _isChecked; }
set { _isChecked = value; OnPropertyChanged("IsChecked"); }
}
#endregion
}
You can get all selected using a simple query
var selectedItems = DescCompuestaList.Where(x => x.IsChecked)
simple as life.
How about binding CheckBox's IsChecked property to ListBoxItem's IsSelected property.
Something like: IsChecked={Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}
In your example:
<ListBox ItemsSource="{StaticResource ResourceKey=lstMaterialesCL}" SelectionMode="Multiple" Name="lstMaterial" >
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Name="chkMaterial" Content="{Binding DescCompuesta}" IsChecked={Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
For me it's always easiest way to use Blend for making templates.
Open project in blend and make one listBox, then select that ListBox and add template like in picture.
just to show purpose i have add simple checkBox and TextBlock, and you can make it as u like it.
in ViewModel i have made simple observable collection just to show purpose and bound ItemsSource to Users:
public class TestVM
{
public ObservableCollection<User> Users { get; set; }
public TestVM()
{
Users = new ObservableCollection<User>
{
new User{ IsChecked=true, Name="User1" },
new User{ IsChecked=false, Name="User2" },
new User{ IsChecked=true, Name="User3" },
new User{ IsChecked=false, Name="User3" },
};
}
}
public class User
{
public bool IsChecked { get; set; }
public string Name { get; set; }
}
This way you can make any template you like.
Wow, I tested all your suggestions, thank you so much to all, after all tests, the solution how I need it is more or less like this:
On xaml:
<StackPanel Orientation="Horizontal">
<ListBox ItemsSource="{Binding MaterialesVM}" SelectionMode="Multiple"
Name="ListBoxMateriales" Width="300" Height="200"
HorizontalAlignment="Left" VerticalAlignment="Top">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding IsChecked, Mode=TwoWay}" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}"
Content="{Binding DescCompuesta}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Name="btnPrueba" Style="{StaticResource BotonContent}"
Content="Selected" Width="80" Height="30" HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="10,0,0,0" Click="btnPrueba_Click"/>
<Button Name="btnLimpia" Style="{StaticResource BotonRechazar}"
Width="30" Height="30" Click="btnLimpia_Click" HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="5,0,0,0" ToolTipService.ToolTip="Limpia Todos"/>
<Button Name="btnMarca" Style="{StaticResource BotonAceptar}"
Width="30" Height="30" Click="btnMarca_Click" HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="5,0,0,0" ToolTipService.ToolTip="Selecciona Todos"/>
</StackPanel>
On this way I don't care on where clicked, the item will be selected or un-selected (on the CheckBox and on the ListItem)
And to see the items selected:
// To show the selected items
private void btnPrueba_Click(object sender, RoutedEventArgs e)
{
var selectedItems = MaterialesVM.Where(x => x.IsChecked);
foreach (var item in selectedItems)
{
MessageBox.Show(((MaterialesCL)item).DescCompuesta);
}
}
To select all items:
// To select all items
private void btnMarca_Click(object sender, RoutedEventArgs e)
{
var Items = ListBoxMateriales.Items;
foreach (MaterialesCL item in Items)
{
item.IsChecked = true;
}
}
To un-select all items:
// To un-select all items
private void btnLimpia_Click(object sender, RoutedEventArgs e)
{
var Items = ListBoxMateriales.Items;
foreach (MaterialesCL item in Items)
{
item.IsChecked = false;
}
}
and my classes are:
MaterialesCL.cs
public class MaterialesCL : INotifyPropertyChanged
{
public int Material { get; set; }
public string Descripcion { get; set; }
public string DescCompuesta { get; set; }
private bool _ischecked;
public bool IsChecked
{
get { return _ischecked; }
set
{
_ischecked = value;
OnPropertyChanged("IsChecked");
}
}
#region PropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
LstMaterialesCL.cs
public class LstMaterialesCL : ObservableCollection<MaterialesCL>, INotifyPropertyChanged
{
public LstMaterialesCL()
{
BasculaEntities _context = new BasculaEntities();
var Query = (from m in _context.Materiales
select new { m.Material, m.Descripcion}
).OrderBy(m => m.Material).ToList();
foreach (var item in Query)
{
this.Add(new MaterialesCL { Material = item.Material,
Descripcion = item.Descripcion, DescCompuesta = item.Material.ToString("000") + " - " + item.Descripcion,
IsChecked=false});
}
}
}
and on my UserControl MaterialesVM is an instance of LstMaterialesCL
LstMaterialesCL MaterialesVM = new LstMaterialesCL();
and so my test of ListBox with CheckBoxes works as I need.
Thank to all you I has learn so much.

Not able to see binded items to listbox

I've created a ListBox with this structure:
<ListBox VerticalAlignment="Stretch"
Background="AliceBlue"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ItemsSource="{Binding EventInfo}">
how you can see I binded the EventInfo property that I valorize behind code. This property have the OnPropertyChange(); implementation as my other properties, and the value setted is got correctly. Anyway, I'm not able to display the binded source:
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=League}" />
<TextBlock Text="test" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
now the property League value isn't displayed also the value test. I really don't understand why. The League property exist, and also I've no error in xaml.
What I did wrong?
UPDATE:
public Models.EventInfo EventInfo
{
get { return _eventInfo; }
set
{
_eventInfo = value;
OnPropertyChanged();
}
}
and in the Model
public class EventInfo
{
public string League { get; set; }
public string Date { get; set; }
public string GameWeek { get; set; }
public string GameStart { get; set; }
public string FirstTime { get; set; }
public string SecondTime { get; set; }
public string Stadium { get; set; }
public List<MatchArbiter> Arbiter { get; set; }
}
Try this. You need to populate ItemsSource with a collection, not a single item. Instead of your existing EventInfo property, you need a collection property. I'm going to rename it to EventInfoItems to keep confusion to a minimum.
private ObservableCollection<Models.EventInfo> _eventInfoItems =
new ObservableCollection<Models.EventInfo>();
public ObservableCollection<Models.EventInfo> EventInfoItems
{
get { _eventInfoItems; }
set
{
_eventInfoItems = value;
OnPropertyChanged();
}
}
Now, somewhere, you're going to have to add some items to that collection if you want anything to appear in the list. You could create a few test items in your viewmodel constructor, just for the time being. Like this:
EventInfoItems.Add(new EventInfo { League = "NBA" });
EventInfoItems.Add(new EventInfo { League = "Premier League" });
EventInfoItems.Add(new EventInfo { League = "Serie A" });
XAML
<ListBox
VerticalAlignment="Stretch"
Background="AliceBlue"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ItemsSource="{Binding EventInfoItems}"
>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=League}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
Update
Turns out OP may have only one item. If that's the case, a ListBox is unnecessary. A ContentControl is the right control when you've got only one item and you want to display it with a DataTemplate. This XAML will use the original version of the EventInfo property:
public Models.EventInfo EventInfo
{
get { return _eventInfo; }
set
{
_eventInfo = value;
OnPropertyChanged();
}
}
XAML:
<ContentControl
VerticalAlignment="Stretch"
Background="AliceBlue"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
Content="{Binding EventInfo}"
>
<ContentControl.ContentTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=League}" />
</StackPanel>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>

Categories

Resources