I know that the ListBox has both a SelectedItem and SelectedItems attribute and that only the SelectedItem attribute can be used with databinding. However, I've read in multiple locations that by setting up a setter like so
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
</Style>
</ListBox.ItemContainerStyle>
and then by adding the following property
public IEnumerable<Item> SelectedItems
{
get
{
return Items.Where(x => x.IsSelected);
}
}
I can use the SelectedItems property to get the all items that were selected. I mainly have two questions regarding this:
Where does the Item and Items in the property come from? I've not been able to find a using directive that will remove the error preventing me from using it.
Is this the recommended way to do what I'm trying to achieve. If not, then what would you recommend?
Oh, before I forget I thought I should mention that I'm using both MVVM Light and Fody's PropertyChanged.
I'm not quite sure about the articles and their exact solution for doing so but my speculations is this:
The whole page has a ViewModel named MyPageViewModel.
MyPageViewModel has an ObservableCollection named Items.
Item is ViewModel type (derived from DependencyObject)
Item has a DependencyProperty named IsSelected
Given these assumptions you can see where can all things fit.
If the DataContext of the whole page is MyPageViewModel, then in this xaml code first IsSelected refers to a property of ListBoxItem, and the second one refers to a property in ViewModel of Item.
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
//This is inside MyPageViewModel
//Item is a ViewModel type
public IEnumerable<Item> SelectedItems
{
get
{
//Items is ObservableCollection<Item>
return Items.Where(x => x.IsSelected);
}
}
//This is also inside MyPageViewModel
private ObservableCollection<Item> _items = new ObservableCollection<Item>();
public ObservableCollection<Item> Items { get { return _items; } }
The whole scenario can go the other way too. I mean it can be implemented in View instead of ViewModel. Maybe derive from ListBox and override a few things including SelectedItems. But adding these sorts of complexities are better done to ViewModel than View.
Related
I have a UserControl that needs to be bind with two DataContext on the basis of a checkbox.
This UserControl have to display the data about the application (global) or selected DataGridRow.
class Person
{
public string Name {get; set;}
public string Age {get; set;}
}
UserControl has only two text fields to display name and Age. If "Global" checkbox is checked, then I want to bind this usercontrol with the property of APerson (of Person class) in MainViewModel and if it is unchecked then I have to bind the UserControl with the SelectedItem in DataGrid. SelectedItem is also a Person type
Basically you could just play with your ViewModel to get what you want. Here is one way to do it.
You would be binding the Checkbox.IsChecked to 'IsGlobal' property in the ViewModel.
Then you would be binding the userControl to another property, say SomePerson in the ViewModel.
Lastly, in the setter of IsGlobal you would change the SomePerson to either APerson or SelectedItem in your datagrid depending on the boolean state of IsGlobal.
[Adding this here since you want a way to do it purely in XAML. I think that insisting on pure XAML here is not essential and #bit's answer is the right way to go, IMO.]
You can use style to have triggers that do the change.
Let's say your UC is called MyUC and currently you have an instance of it similar to: <local:MyUC/> in some other view/UC/window. You can change the instance to look like so:
<local:MyUC>
<local:MyUC.Style>
<Style TargetType="{x:Type local:MyUC}">
<Setter Property="DataContext" Value="{Binding SelectedItem, ElementName=MyDataGrid}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MyCheckbox, Path=IsChecked}"
Value="True">
<Setter Property="DataContext" Value="{Binding APerson}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</local:MyUC.Style>
</local:MyUC>
I'm changing here the data context property, but you can change any other dependency property on MyUC.
Again, I think this is a less favorite approach to tackle this functionality, but it's pure XAML.
I'm writing a tag control, based on the listbox.
It is displaying the ListBox items using following template:
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<local:TagControl Text="{Binding Path=., Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Remove="RemoveItem" />
</DataTemplate>
</Setter.Value>
</Setter>
I've noticed that when I update TagControl's text, the original item in the ListBox does not get updated. I'm using ObservableCollection<string> as items source.
TagControl implements INotifyPropertyChanged and calls the event.
What am I doing wrong?
I've reproduced your problem and can offer a solution. The ObservableCollection<string> is enumerated using IEnumerable which is read-only.
If you replace the ObservableCollection<string> with ObservableCollection<DataItem> where
public class DataItem
{
public string Name{get;set;}
}
and then bind to Name in your DataTemplate, the enumerated DataItem is read-only, but the Name property is read-write and will be updated when you edit the text in the list item.
I'm struggling for about 14 days now with a simple task: In database, I have definitions for hardware categories. For example :
HDD
Internal
External
Flash
This list is in database defined like this:
[ID - ParrentID - Name] : 1 - 0 - HDD, 2 - 1 - Internal, 3 - 1 - External, 4 - 1 - Flash.
Through Entity Framework I get these rows into my application. From this flat data I then create structured object which is my DataModel. This model is defined as follows :
public class Category
{
private int _id = -1;
private string _name = "";
private List<Category> _subCategories = null;
// property getters and setters, constructors, and bool HasSubCategories
}
Now, from these I create ViewModel called SubCategoryViewModel to which is binded my TreeView. So, I can view my categories in treeview and with my defined and maintained hierarchy. This works just fine. In SubCategoryViewModel is defined a Command through Attached Behavior for MouseDoubleClick which is also binded to TreeView. So, when user doubleclicks on Item, in SubViewCategoryModel defined method will execute particular code. List of SubCategoryViewModel is nested in HWDocumentViewModel which is a main ViewModel for my window.
What I need now is obvious : When user doubleclicks on item in TreeView, I need to load items from database and show them in ListView. My opinion is, that in HWDocumentViewModel I need to define an collection of Items and load them accordingly to selected category in ListView. But, I don't know how to execute a method on HWDocumentViewModel from SubCategoryViewModel. Because : TreeView is binded to list of SubCategoryViewModel items, so when DoubleClick occurs, the method on SubCategoryViewModel is executed. I'm searching for a way, how to execute a method on main ViewModel (HWDocumentViewModel).
I tried this approach :
I created a property : public static SubCategoryViewModel SelectedCategory on HWDocumentViewModel. When doubleclick occurs, I set this property from SubCategoryViewModel as this. So, in this property is object, which executed doubleclick event delegate. Great, now I have in HWDocumentView model an object, which user selected.
So, I need to load items to ListView. But, will I load them from method in SubCategoryViewModel ? I don't think so. Instead I should load them from Main View Model by creating a ViewModel for them and bind it to ListView, right ? But, how can I from SubCategoryViewModel call a method in HWDocumentViewModel ? Should I write a static method
on a HWDocumentViewModel which will be accessible from SubCategoryViewModel ?
Or is there a way, how to call Command defined on HWDocumentViewModel from SubCategoryViewModel ?
Or generally, did I take a right approach to create a Warehouse-like application in WPF ?
Thanks a lot.
EDIT: XAML for my TreeView looks like this :
<TreeView x:Name="tvCategories" Background="White" ItemsSource="{Binding Categories}">
<TreeView.ItemContainerStyle>
<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" />
<Setter Property="behaviors:MouseDoubleClick.Command" Value="{Binding MouseDoubleClickCommand}" />
<Setter Property="behaviors:MouseDoubleClick.CommandParameter" Value="{Binding}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type localvm:SubCategoryViewModel}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding CategoryName}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
I'm not sure I see the problem. You have a tree of subcategories and when one is selected, the appropriate SubCategoryViewModel sets itself as SelectedCategory on the main HWDocumentViewModel. That seems like a reasonable approach.
So why do you need to call a command? Why can't you just load the new list in HWDocumentViewModel in response to a change of its SelectedCategory property (ie in the setter)?
If you really must use a command to invoke the load, then simply keep a reference to your main HWDocumentViewModel in each SubCategoryViewModel, and invoke the command with a simple:
_mainViewModel.LoadCategoryCommand.Execute();
With MVVM and trying to communicate between View and ViewModel or between ViewModels a publisher/Subscriber setup works well or a messaging paradigm like what's found in MVVMLight or Prism. I posted an answer on MVVM Light's messaging setup here
In the message you can send an object that holds any data you would like to send back and forth between the view models.
I highly recomend using a framework when working with mvvm it makes like much easier. MVVM Framework Comparison is a link to an answer that goes through a comparison of some of the major frameworks.
I have a list box in WPF as under
<ListBox Name="lstName" DisplayMemberPath ="ListName" ToolTip="{Binding Path=ListName}" />
My requirement is that what ever items I am displaying in the listbox, should also appear in the tooltip. i.e. if the items are say "Item1", "Item2" etc. then when the user will point(hover) to "Item1" through mouse, the toolltip should display "Item1". Same for others
So my DisplayMemberPath is set to the Property which I am supposed to display (and it is coming properly). However, the tooltip is not coming at all.
The entity is as under
public class ItemList
{
public string ListName { get; set; }
}
The binding is happening as under
this.lstName.ItemsSource = GetData(); // Assume that the data is coming properly
Instead of setting the ToolTip property on the ListBox, set it on the ListBoxItems by applying a style:
<ListBox Name="lstName" DisplayMemberPath="ListName">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ToolTip" Value="{Binding ListName}"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
That way, each ListBoxItem will have its own tooltip that displays the value for that item.
Since you are setting the ItemsSource on the ListBox directly, you probably haven't set a DataContext, so the Binding won't work there. If you do set the DataContext to the list, then that binding would display the currently selected item as the tooltip no matter where the mouse was on the ListBox.
I've spent far too much time with this and can't find the mistake. Maybe I'm missing something very obvious or I may have just found a bug in the WPF Element Host for Winforms.
I am binding a ListView to a ObeservableList that lives on my ProductListViewModel.
I'm trying to implement searching for the ListView with the general Idea to just change the ObservableList with a new list that is filtered.
Anyway, the ListView Binding code looks like this:
<ListView ItemsSource="{Binding Path=Products}" SelectedItem="{Binding Path=SelectedItem}" SelectionMode="Single">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"></Setter>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And the ViewModel code is as vanilla as it can get:
private ObservableCollection<ProductViewModel> products;
public ObservableCollection<ProductViewModel> Products
{
get { return products; }
private set
{
if (products != value)
{
products = value;
OnPropertyChanged("Products");
}
}
}
Now the problem here: Once I debug into my OnPropertyChanged method, I can see that there are no subscribers to the PropertyChanged event (it's null), so nothing happens on the UI..
I already tried Mode=TwoWay and other Binding modes, it seems I can't get the ListView to subscribe to the ItemsSource...
Can anyone help me with this? I'm just about to forget about the ElemenHost and just do it in Winforms
greetings Daniel
Is there any binding error in the output window?
By the way, you should consider getting the collection view wrapping your products, and then filtering the view, instead of replacing the whole collection.
The code would be something like:
var collectionView = CollectionViewSource.GetDefaultView(Products);
collectionView.Filter += item => ...;