I have created a user control with bind able ListView in it. I bind item source and selected item of this control and it works as expected when I use it once in a view. However when I try to reuse the same user control with different item source and selected item, item selection stops working - when I select item from one ListView item from another ListView gets unselected.
I bind my ListView like this:
<ListView
x:Name="SelectionListView"
Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
ItemsSource="{Binding ElementName=SelectionListControl, Path=Items}"
Background="Transparent" SelectedItem="{Binding ElementName=SelectionListControl, Path=SelectedItem}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="6"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<controls:RadioButtonConfig Content="{Binding Text}"
Template="{StaticResource RadioButtonConfig}"
GroupName="DisplayPage"
IsChecked="{Binding IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}}"
IsConfigured="{Binding IsConfigured}"/>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
I added my code to github ListViewBinding project.
Edit:
ListView control code behind:
public partial class SelectionList : UserControl
{
public List<SelectionListItem> Items
{
get { return (List<SelectionListItem>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register("Items", typeof(List<SelectionListItem>), typeof(SelectionList), new PropertyMetadata(new List<SelectionListItem>()));
public SelectionListItem SelectedItem
{
get { return (SelectionListItem)GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(SelectionListItem), typeof(SelectionList), new FrameworkPropertyMetadata(new SelectionListItem(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public SelectionList()
{
InitializeComponent();
}
}
ListView Item model (SetProperty raises INotifyPropertyChanged PropertyChanged event):
public class SelectionListItem: ObservableObject
{
private string text;
public string Text
{
get { return text; }
set { SetProperty(ref text, value); }
}
private object valueObj;
public object ValueObj
{
get { return valueObj; }
set { valueObj = value; }
}
private bool isConfigured;
public bool IsConfigured
{
get { return isConfigured; }
set { SetProperty(ref isConfigured, value); }
}
}
I use my controls in MainWindow:
<StackPanel Grid.Row="0">
<local:SelectionList
x:Name="List1"
Items="{Binding SelectableItems}"
SelectedItem="{Binding SelectedItem}"/>
<TextBlock >
<Run Text="Selected Item: "/>
<Run Text="{Binding SelectedItem.Text, FallbackValue=SelectedItem}"/>
</TextBlock>
<TextBlock>
<Run Text="Configured Item: "/>
<Run Text="{Binding ConfiguredItem.Text, FallbackValue=ConfiguredItem}"/>
</TextBlock>
</StackPanel>
<StackPanel Grid.Row="1">
<local:SelectionList
x:Name="List2"
Items="{Binding SelectableItems2}"
SelectedItem="{Binding SelectedItem2}"/>
<TextBlock >
<Run Text="Selected Item: "/>
<Run Text="{Binding SelectedItem2.Text, FallbackValue=SelectedItem}"/>
</TextBlock>
<TextBlock>
<Run Text="Configured Item: "/>
<Run Text="{Binding ConfiguredItem2.Text, FallbackValue=ConfiguredItem}"/>
</TextBlock>
</StackPanel>
With data binded from MainWindowViewModel:
public class MainWindowViewModel : ObservableObject
{
#region list1
private List<SelectionListItem> selectableItems = new List<SelectionListItem>();
public List<SelectionListItem> SelectableItems
{
get { return selectableItems; }
set
{
SelectedItem = value[0];
SetProperty(ref selectableItems, value);
}
}
private SelectionListItem selectedItem = new SelectionListItem { Text = "-", IsConfigured = false };
public SelectionListItem SelectedItem
{
get { return selectedItem; }
set { SetProperty(ref selectedItem, value); }
}
private SelectionListItem configuredItem = new SelectionListItem { Text = "-", IsConfigured = false };
public SelectionListItem ConfiguredItem
{
get { return configuredItem; }
set { SetProperty(ref configuredItem, value); }
}
private string valueText;
public string ValueText
{
get { return valueText; }
set { SetProperty(ref valueText, value); }
}
#endregion
#region list1
private List<SelectionListItem> selectableItems2 = new List<SelectionListItem>();
public List<SelectionListItem> SelectableItems2
{
get { return selectableItems2; }
set
{
SelectedItem2 = value[0];
SetProperty(ref selectableItems2, value);
}
}
private SelectionListItem selectedItem2 = new SelectionListItem { Text = "-", IsConfigured = false };
public SelectionListItem SelectedItem2
{
get { return selectedItem2; }
set { SetProperty(ref selectedItem2, value); }
}
private SelectionListItem configuredItem2 = new SelectionListItem { Text = "-", IsConfigured = false };
public SelectionListItem ConfiguredItem2
{
get { return configuredItem2; }
set { SetProperty(ref configuredItem2, value); }
}
private string valueText2;
public string ValueText2
{
get { return valueText2; }
set { SetProperty(ref valueText2, value); }
}
#endregion
}
Related
I want to create a 2 level treview, with the root element of a string "Items":
-Items
-item1
-item2
-...
I have 2 classes to achieve this: ItemList, Item. I call them with the property CurrentItems.
private ItemList _currentItems = null;
public ItemList CurrentItems
{
get
{
return _currentItems;
}
set
{
if(_currentItems!=value)
{
SetProperty(ref _currentItems,
value, () => CurrentItems);
}
}
}
CurrentItems is initialized by creating a temporary ItemsList which is then filled with Items:
ItemList itemTempList = new ItemList();
...
while(...)
{
Item item = new Item();
...
itemTempList.Items.Add(item);
}
CurrentItems = itemTempList;
These are the two classes used:
public class Item
{
public long ObjectID { get; set; }
public string ItemName { get; set; }
}
public class ItemList : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<Item> items = new ObservableCollection<Item>();
public ObservableCollection<Item> Items
{
get
{
return items;
}
set
{
items = value;
OnPropertyChanged();
}
}
private ObservableCollection<string> types = new ObservableCollection<string>();
public ObservableCollection<string> Types
{
get
{
if(types.Count > 0)
{
types.Add("Items");
}
return types;
}
set
{
types = value;
OnPropertyChanged();
}
}
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
The View is initialized with a constructor:
public ItemView()
{
InitializeComponent();
}
And finally, this is the xaml:
<TreeView Name="itemTree" Grid.Row="1" Grid.Column="0" SelectedItemChanged="itemTree_SelectedItemChanged"
ItemsSource="{Binding CurrentItems}">
<TreeView.Resources>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True"/>
</Style>
</TreeView.Resources>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Items,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}">
<Label Content="{Binding Types,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ItemName,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Duplicate" Click="DuplicateCell"/>
<MenuItem Header="Delete" Click="DeleteCell"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
With my current understanding and code I am not able to create the TreeView.
Any help would be greatly appreciated!
My client has got the problem with the size of the CheckBox inside a ListBox. I agree, it's small and not so easy to check at times.
I've tried to find a way to make a CheckBox bigger but I've found out that it's complicated (and would require using Blend, which I don't want to use).
What I want to do though is to check the CheckBox when clicking on a whole item.
[ ] some text
In this example - on "some text" or inside the item wherever. Right now I have to click inside the CheckBox to have it checked.
I generate my CheckBoxes dynamically.
My xamls of this control looks like this:
<ListBox Name="restoredDBsListBox" ItemsSource="{Binding ProperlyRestoredDatabases}" Grid.ColumnSpan="2" HorizontalAlignment="Left" Height="170" Margin="34,160,0,0" VerticalAlignment="Top" Width="276" SelectionMode="Extended">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<CheckBox Name="check" IsChecked="{Binding IsChecked, Mode=TwoWay}" Margin="3" VerticalAlignment="Center"/>
<ContentPresenter Content="{Binding Value}" Margin="1"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and my ViewModel:
/// <summary>
/// Defines the _properlyRestoredDatabases
/// </summary>
private CheckableObservableCollection<string> _properlyRestoredDatabases;
/// <summary>
/// Gets or sets the ProperlyRestoredDatabases
/// </summary>
public CheckableObservableCollection<string> ProperlyRestoredDatabases
{
get { return _properlyRestoredDatabases; }
set
{
_properlyRestoredDatabases = value;
OnPropertyChanged("ProperlyRestoredDatabases");
}
}
CheckableObservableCollection class :
public class CheckableObservableCollection<T> : ObservableCollection<CheckedWrapper<T>>
{
private ListCollectionView _selected;
public CheckableObservableCollection()
{
_selected = new ListCollectionView(this);
_selected.Filter = delegate (object checkObject) {
return ((CheckedWrapper<T>)checkObject).IsChecked;
};
}
public void Add(T item)
{
this.Add(new CheckedWrapper<T>(this) { Value = item });
}
public ICollectionView CheckedItems
{
get { return _selected; }
}
internal void Refresh()
{
_selected.Refresh();
}
}
and CheckedWrapper
public class CheckedWrapper<T> : INotifyPropertyChanged
{
private readonly CheckableObservableCollection<T> _parent;
public CheckedWrapper(CheckableObservableCollection<T> parent)
{
_parent = parent;
}
private T _value;
public T Value
{
get { return _value; }
set
{
_value = value;
OnPropertyChanged("Value");
}
}
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
_isChecked = value;
CheckChanged();
OnPropertyChanged("IsChecked");
}
}
private void CheckChanged()
{
_parent.Refresh();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler pceh = PropertyChanged;
if (pceh != null)
{
pceh(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The CheckBox has a Content property so there is no reason to use a separate ContentPresenter. If you also add an ItemContainerStyle that stretches the ListBoxItem container horizontally, you will be able to check and uncheck the CheckBox by clicking anywhere on the row:
<ListBox Name="restoredDBsListBox" ItemsSource="{Binding ProperlyRestoredDatabases}" Grid.ColumnSpan="2" HorizontalAlignment="Left" Height="170" Margin="34,160,0,0" VerticalAlignment="Top" Width="276" SelectionMode="Extended">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Name="check" IsChecked="{Binding IsChecked, Mode=TwoWay}"
Content="{Binding Value}"
Margin="3"
VerticalAlignment="Center"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This question already has answers here:
How to use DataTemplateSelector with ContentControl to display different controls based on the view-model?
(2 answers)
Closed 4 years ago.
I'm trying to write a simple dialog that would accept a value in a SpinEdit or a text in a TextEdit. I'm using multiple VMs and I made a selector that should view a proper control based on the logic in the c++/cli file.
XAML:
xmlns:local="clr-namespace:asd"
Title="{Binding Path=Title, Mode=OneTime}"
<dx:DXWindow.Resources>
<DataTemplate x:Key="TInputValueVM" DataType="{x:Type local:TInputValueVM}">
<dxe:SpinEdit Height="23" Width="200"
Text="{Binding Value, Mode=TwoWay}"
Mask="{Binding Mask, Mode=OneWay}"
MaxLength="{Binding Path=InputLength}" />
</DataTemplate>
<DataTemplate x:Key="TInputTextVM" DataType="{x:Type local:TInputTextVM}">
<dxe:TextEdit Height="23" Width="200"
Text="{Binding Value, Mode=TwoWay}"
MaskType="RegEx" Mask="{Binding Mask, Mode=OneWay}"
MaxLength="{Binding Path=InputLength}"/>
</DataTemplate>
<local:PropertyDataTemplateSelector x:Key="templateSelector"
DataTemplate_Value="{StaticResource TInputValueVM}"
DataTemplate_Text="{StaticResource TInputTextVM}" />
</dx:DXWindow.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" >
<Label x:Uid="Label" MinHeight="24" MinWidth="60" Content="Value" />
<ContentControl Content="{Binding Path=Whoami}" ContentTemplateSelector="{StaticResource templateSelector}" />
</StackPanel>
<StackPanel Grid.Row="1" x:Uid="OKCancel_Buttons" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Bottom">
<Button Height="23" x:Name="OK_Button" Click="OK_Click" Content="OK" IsDefault="True" HorizontalAlignment="Right" MinWidth="95" />
<Button Height="23" x:Name="Cancel_Button" Click="Cancel_Click" Content="Cancel" HorizontalAlignment="Right" MinWidth="95" />
</StackPanel>
</Grid>
In c# I have a base VM and two VMS that extend it, one for values and one for text. The rest of the properties stay the same.
C#
namespace asd
{
public class TInputBaseVM : ViewModelBase
{
private string m_sTitle;
private string m_sMask;
private int m_nInputLenght;
private string m_sWhoami;
public TInputBaseVM(string A_sTitle, string A_sMask, int A_nInputLength)
{
m_sTitle = A_sTitle;
m_sMask = A_sMask;
m_nInputLenght = A_nInputLength;
}
protected string Title
{
get { return m_sTitle; }
set { SetProperty(ref m_sTitle, value, () => Title); }
}
protected string Mask
{
get { return m_sMask; }
set { SetProperty(ref m_sMask, value, () => Mask); }
}
protected int InputLength
{
get { return m_nInputLenght; }
set { SetProperty(ref m_nInputLenght, value, () => InputLength); }
}
protected string Whoami
{
get { return m_sWhoami; }
set { SetProperty(ref m_sWhoami, value, () => Whoami); }
}
}
public class TInputValueVM : TInputBaseVM
{
public TInputValueVM(string A_sTitle, string A_sMask, int A_nInputLength, double A_nValue) : base(A_sTitle, A_sMask, A_nInputLength)
{
Value = A_nValue;
Whoami = "Value";
}
private double m_nValue;
public double Value
{
get { return m_nValue; }
set { SetProperty(ref m_nValue, value, () => Value); }
}
}
public class TInputTextVM : TInputBaseVM
{
public TInputTextVM(string A_sTitle, string A_sMask, int A_nInputLength, string A_sValue) : base(A_sTitle, A_sMask, A_nInputLength)
{
Value = A_sValue;
Whoami = "Text";
}
private string m_sValue;
public string Value
{
get { return m_sValue; }
set { SetProperty(ref m_sValue, value, () => Value); }
}
}
public class PropertyDataTemplateSelector : DataTemplateSelector
{
public DataTemplate DataTemplate_Value { get; set; }
public DataTemplate DataTemplate_Text { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var selector = item as string;
if(selector == "Value")
return DataTemplate_Value;
return DataTemplate_Text;
}
}
}
In c++/cli I create an object of a proper VM and I'd like the WPF to automatically update the view to either spinedit or textedit, however I'm not sure how to properly bind the properties from the C#. If I explicitly type 'Value' in the Content property of the ContentControl then it displays the spinEdit but I don't know how to bind it so it automatically takes the correct property.
EDIT: I'm adding c++/cli code to show how I choose different VMs
C++/cli:
bool TSignalNumberPositionDialogCLR::StartDialog(TSignalNumberPositionSupport& A_Attributes, HWND A_hwndParent, LPTSTR String)
{
try
{
TInputValueVM ^oExchange_Value;
TInputTextVM ^oExchange_Text;
int inputFormat = A_Attributes.GetInputFormat();
if(inputFormat)
oExchange_Text = gcnew TInputTextVM(gcnew System::String(A_Attributes.GetTitle()), gcnew System::String(A_Attributes.GetMask()),
A_Attributes.GetInputLength(), gcnew System::String(A_Attributes.GetInitialText()));
else
oExchange_Value = gcnew TInputValueVM(gcnew System::String(A_Attributes.GetTitle()), gcnew System::String(A_Attributes.GetMask()),
A_Attributes.GetInputLength(), A_Attributes.GetInitialValue());
Dialogs::TSignalNumberPositionDialog^ dialog = gcnew Dialogs::TSignalNumberPositionDialog();
if(inputFormat)
dialog->DataContext = oExchange_Text;
else
dialog->DataContext = oExchange_Value;
dialog->ShowDialog();
if(dialog->DialogResult)
{
CString nValue;
if(inputFormat)
nValue = oExchange_Text->Value;
else
nValue = ((Decimal)oExchange_Value->Value).ToString("F2", CultureInfo::InvariantCulture);
A_Attributes.UpdateValue(nValue, String, A_Attributes.GetInputLength());
return true;
}
return false;
}
catch(Exception^ e)
{
e;
}
}
based on the 'inputFormat' variable I want to display different controls in the dialog.
EDIT: Based on #Clemens comments I got rid of the selector sectionand the x:Key property in the DataTemplates. I changed the content opf the Content property to Content="{Binding}" and it somehow works. The moment I create a VM it selects the correct one.
Based on your comment. Let me improve my answer. As you are facing issue in VM selection. so plesae concentrate how I assigned VM to datatemplate. Although it is done in very basic way, you can handle it if you you are using MVVM packages.
I have created 2 data template and 2 vms and each vm is bound to datatemplate. To verify, I have a combobox, which will select datatemplate based on selected value.
Here is Sample VM
public class VM : System.ComponentModel.INotifyPropertyChanged
{
private string title;
private SolidColorBrush background;
public string Title { get => title; set { title = value; RaisePropertyChanged(); } }
public SolidColorBrush Background { get => background; set { background = value; RaisePropertyChanged(); } }
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string name = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
public class VM1: VM
{
public VM1()
{
Title = "This is VM1";
Background = Brushes.Yellow;
}
}
public class VM2: VM
{
public VM2()
{
Title = "This is VM2";
Background = Brushes.Orange;
}
}
Now check for resources
<local:VM1 x:Key="VM1"/>
<local:VM2 x:Key="VM2"/>
<DataTemplate x:Key="DT1">
<Grid DataContext="{StaticResource VM1}">
<TextBlock Text="{Binding Title}" Background="{Binding Background}"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="DT2">
<Grid DataContext="{StaticResource VM2}">
<TextBlock Text="{Binding Title}" Background="{Binding Background}"/>
</Grid>
</DataTemplate>
<Style TargetType="ContentControl" x:Key="contentStyle">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=cmbo, Path=SelectedValue}" Value="Template1">
<Setter Property="ContentTemplate" Value="{StaticResource DT1}" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=cmbo, Path=SelectedValue}" Value="Template2">
<Setter Property="ContentTemplate" Value="{StaticResource DT2}" />
</DataTrigger>
</Style.Triggers>
</Style>
and finally I have combobox and content control just to verify
<ComboBox Name="cmbo"/>
<ContentControl Style="{StaticResource contentStyle}"/>
where cmbo.ItemsSource = new List { "Template1", "Template2" };
Hope you got the point
I have a Button which shows a ContextMenu look like
Here the XAML:
<Window.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding}" />
<Style x:Key="MenuItemStyle" TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding Data.OnSelected, Source={StaticResource proxy}}" />
</Style>
<Button Name="ButtonMenu_Export"
Click="ButtonMenu_Export_Click"
Visibility="{Binding ButtonExportEnabled,
Converter={StaticResource VisibilityConverter}}">
<StackPanel Orientation="Vertical">
<Image Source="...." />
<TextBlock Width="70" TextAlignment="Center" Text="Export" />
</StackPanel>
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding ExportMenuItems}">
<ContextMenu.ItemTemplate>
<HierarchicalDataTemplate ItemContainerStyle="{StaticResource MenuItemStyle}">
<ContentPresenter Content="{Binding Text}" RecognizesAccessKey="True" />
<HierarchicalDataTemplate.ItemsSource>
<Binding Path="SubItems" />
</HierarchicalDataTemplate.ItemsSource>
</HierarchicalDataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</Button.ContextMenu>
</Button>
The menu is created at runtime using this List (as in this article)
public System.Collections.Generic.List<MenuItem> ExportMenuItems
{
get { return _menuService.GetParentMenuItems(); }
}
Now, what I cannot do is bind the items to the OnSelected command of MenuItem class.
The class which defines the menu is:
public class MenuItem
{
private string name;
private string text;
private int menuId;
private ICommand onSelected;
private MenuItem parent;
private ObservableCollection<MenuItem> subItems;
public MenuItem(string name, string text, int MenuId)
{
this.menuId = MenuId;
this.name = name;
this.text = text;
this.subItems = new ObservableCollection<MenuItem>();
}
public string Name { get { return this.name; } }
public string Text { get { return this.text; } }
public MenuItem Parent { get { return this.parent; } set { this.parent = value; } }
public ICommand OnSelected
{
get
{
if (this.onSelected == null)
this.onSelected = new MenuCommand(this.ItemSelected, this.ItemCanBeSelected, menuId);
return this.onSelected;
}
}
public ObservableCollection<MenuItem> SubItems
{
get { return this.subItems; }
}
}
I created a proxy class as in this article to made DataContext visible to HierarchicalDataTemplate content but maybe I misunderstood something:
public class BindingProxy : Freezable
{
#region Overrides of Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
Where I'm wrong?
Change your command binding
Command="{Binding Data.OnSelectedd,Source={StaticResource proxy}, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=MenuItem}"
I have a ListBox with a GroupStyle that contains another ListBox.
Now I want to filter the Items of the nested ListBox depending on the group name of the parent ListBox.
In the code below I tried to chain the GroupItem.Name.Name property to the GroupName property of the ViewModel of the nested ListBox, but this didn't work out so well.
Essentially the GroupNameIn Property is filled by the GroupItems' name(the TextBlock Text) and then sets the GroupNameOut Property to the same value in the PropertyChangedCallback. But the problem is that the GroupName Property of the NestedItemsViewModel to which GroupNameOut is bound to doesn't update.
Are there some mistakes in my approach or is there even a simpler/better way to achieve this behavior?
I would be very grateful if someone could point me in the right direction.
GroupStyle of the parent ListBox:
<Style x:Key="MyListBoxGroupStyle" TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<StackPanel Name="container" Width="Auto" Orientation="Vertical">
<TextBlock Name="groupNameTextBlock" Text="{Binding Path=Name.Name}"/>
<ItemsPresenter/>
<MyNestedListBox
DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBox}}, Path=NestedItemsDataContext}"
ItemsSource="{Binding NestedItems}"
GroupNameIn="{Binding ElementName=groupNameTextBlock, Path=Text}"
GroupNameOut="{Binding Path=GroupName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The nested ListBox:
public class MyNestedListBox : ListBox
{
static MyNestedListBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyNestedListBox), new FrameworkPropertyMetadata(typeof(MyNestedListBox)));
}
public string GroupNameIn
{
get { return (string)GetValue(GroupNameInProperty); }
set { SetValue(GroupNameInProperty, value); }
}
public string GroupNameOut
{
get { return (string)GetValue(GroupNameOutProperty); }
set { SetValue(GroupNameOutProperty, value); }
}
// DepenencyProperties
public static readonly DependencyProperty GroupNameInProperty =
DependencyProperty.Register("GroupNameIn", typeof(string), typeof(MyNestedListBox), new UIPropertyMetadata(null) { PropertyChangedCallback = (obj, target) =>
{
obj.SetValue(GroupNameOutProperty, target.NewValue);
}
});
public static readonly DependencyProperty GroupNameOutProperty =
DependencyProperty.Register("GroupNameOut", typeof(string), typeof(MyNestedListBox), new UIPropertyMetadata(null));
}
ViewModel bound to the nested ListBox:
public class NestedItemsViewModel : ViewModelBase
{
private string _groupName;
public ObservableCollection<NestedItem> NestedItems { get; set; }
public string GroupName
{
get
{
return _groupName;
}
set
{
_groupName = value;
OnPropertyChanged(() => GroupName);
}
}
public NestedItemsViewModel()
{
NestedItems = new ObservableCollection<NestedItem>();
}
}