I have a view which wraps a TreeView, called MbiTreeView. I want to get the selected item from the (wrapped) tree view in the view model.
The 'parent' user control which uses this custom user control:
<UserControl [...]>
<views:MbiTreeView
Grid.Row="0"
cal:Bind.Model="{Binding TreeViewModel}"
SelectedItem="{Binding SelectedItem}">
</views:MbiTreeView>
</UserControl>
The parent user control is bound to this view model:
internal sealed class SomeViewModel : PropertyChangedBase
{
public object SelectedItem
{
get => _selectedItem;
set
{
_selectedItem = value;
NotifyOfPropertyChange(() => SelectedItem);
}
}
public IMbiTreeViewModel TreeViewModel { get; }
public SomeViewModel(
IMbiTreeViewModel treeViewModel)
{
TreeViewModel = treeViewModel;
}
}
The MbiTreeView user control is rather straight forward. It subscribes to the selection changed event, and defines a few templates (not relevant for this question, so left them out in the question)
<TreeView ItemsSource="{Binding Items}" SelectedItemChanged="TreeView_OnSelectedItemChanged">
iew.ItemContainerStyle>
The code behind declares the dependency property:
public partial class MbiTreeView
{
public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register(
nameof(SelectedItem),
typeof(object),
typeof(MbiTreeView),
null);
public object SelectedItem
{
get => GetValue(SelectedItemProperty);
set => SetValue(SelectedItemProperty, value);
}
public MbiTreeView()
{
InitializeComponent();
}
private void TreeView_OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
SelectedItem = e.NewValue;
}
}
when I start the application, I can navigate through the tree view items. When I click on a treeview node, then the OnSelectedItemChanged event fires (I get into my breakpoint there). So everything goes fine up and until setting the value in the dependency property SelectedItem.
Then I would expect that the xaml binding gets notified, and updates the view model. But that never happens.
I am getting nowhere with this, help is greatly appreciated.
The SelectedItem Binding should be TwoWay:
<views:MbiTreeView ...
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"/>
You could declare the property like shown below to make to bind TwoWay by default.
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register(
nameof(SelectedItem),
typeof(object),
typeof(MbiTreeView),
new FrameworkPropertyMetadata(
null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
Related
I have a multiselect Combobox usercontrol and a dependency property 'SelectedItems'.
I m trying to use the usercontrol and bind the 'SelectedItems' to another property called 'SelectedResultItems' in my ViewModel. But I dont get any values to SelectedResultItems. Please help
Here is what i tried.
My main xaml:
<DataTemplate x:Key="TypeATemplate">
<control:MultiSelectComboBox Width="315" ItemsSource="{Binding
ResultvalueList,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
SelectedItems="{Binding
SelectedResultItems,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
My Combobox usercontrol code behind:
public static readonly DependencyProperty SelectedItemsProperty =
DependencyProperty.Register("SelectedItems",
typeof(ObservableCollection<string>), typeof(MultiSelectComboBox), new
FrameworkPropertyMetadata(null,new
PropertyChangedCallback(MultiSelectComboBox.OnSelectedItemsChanged)));
public ObservableCollection<string> SelectedItems
{
get { return
(ObservableCollection<string>)GetValue(SelectedItemsProperty); }
set
{
SetValue(SelectedItemsProperty, value);
}
}
I am setting the 'SelectedItems' on click of the checkbox.
My mainviewmodel:
public ObservableCollection<string> SelectedResultItems
{
get => _selectedResultItems;
set
{
_selectedResultItems = value;
NotifyPropertyChanged(nameof(SelectedResultItems));
}
}
If this is the same as for ListView(never used MultiSelectCombobox), you cannot bind to SelectedItems because it is a read-only property.
What I did to solve that is add the event SelectionChanged to ListView(or MultiSelectCombobox for you).
Then event would be :
private void YourComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
contexte.ResultItems = YourComboBox.SelectedItems.Cast<YourItem>().ToList();
}
Maybe there is a different way to do it, but until now that's the easiest way I found.
I cannot properly bind to a UserControl property placed in a Page.
I have this UserControl :
<UserControl x:Class="xxxx.NumericBox" (...)>
<TextBox Name="TextBoxValue" Text="{Binding RelativeSource {RelativeSource AncestorType=UserControl}, Path=Value, Mode=TwoWay}" (...)
With this behind code :
public partial class NumericBox : UserControl
{
public NumericBox()
{
InitializeComponent();
}
public uint? Value
{
get => (uint?)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(uint?), typeof(NumericBox), new PropertyMetadata(null));
The UserControl contains others controls witch interract with Value property (+/-) and it works fine.
But I create the DependencyProperty to also bind the value in parent page.
A exemple of code in a page where I inject the UserControl :
var binding = new Binding("Line.Quantity");
binding.Mode = BindingMode.TwoWay;
var numeric = new NumericBox();
numeric.SetBinding(ValueProperty, binding);
The binding works on startup but not update Line.Quantity when I modify the Textbox...
The Line class implements INotifyPropertyChanged and notify change on Quantity.
What is the correct way to do that ?
I have seen this question but but I have not been able to correct my code :
Binding on DependencyProperty of custom User Control not updating on change
I have UserControl, lets call it as CustomDataGrid, that contains DataGrid. Remained content doesn't matter. SelectedItem property of DataGrid must be SelectedItem property of CustomDataGrid. And I wanna be able to use Binding with this property, cause I use MVVM pattern. So I have to declare SelectedItem as DependencyProperty in CustomDataGrid. But I have no ideas haw can I make it properly...
This is how DepedencyProperty-s is declared usually:
public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register(
"SelectedItem", typeof(Object), typeof(CustomDataGrid),
new FrameworkPropertyMetadata(default(Object), SelectedItemPropertyCallback)
{
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
});
// Optionally
private static void SelectedItemPropertyCallback(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
// dataGrid - `DataGrid` nested in `UserControl`
((CustomDataGrid)obj).dataGrid.SelectedItem = e.NewValue;
}
// Obviously, it has no any link with nested `dataGrid`. This is the problem.
public Object SelectedItem
{
get { return GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
So, how can I declare SelectedItem property correctly?
You could leverage the binding framework for wiring such properties from underlying objects to outer containers
example assuming CustomDataGrid as UserControl
class CustomDataGrid : UserControl
{
public CustomDataGrid()
{
Binding b = new Binding("SelectedItem");
b.Source = this;
b.Mode = BindingMode.TwoWay;
dataGrid.SetBinding(DataGrid.SelectedItemProperty, b);
}
public object SelectedItem
{
get { return (object)GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedItem. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(object), typeof(CustomDataGrid), new PropertyMetadata(null));
}
I have created a property called SelectedItem in CustomDataGrid and set a two way binding to SelectedItem of the actual dataGrid inside.
so this will wire up these properties and will propagate any changes to and fro.
XAML solution!
Use this DependencyProperty:
public object SelectedItem
{
get { return GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem", typeof(object), typeof(CustomDataGrid ), new FrameworkPropertyMetadata(null)
{
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
});
Then make your outer CustomDataGrid UserControl XAML look like this:
<UserControl x:Class="CustomDataGrid">
<DataGrid ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type CustomDataGrid}}}"
SelectedItem="{Binding SelectedItem, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type CustomDataGrid}}}">
</DataGrid>
</UserControl>
You can then use the CustomDataGrid control the same way as the DataGrid control when binding ItemsSource and SelectedItem to your view model.
Using the MVVM pattern, I've bound the SelectedIndex property of a combo box to a variable in my view model. I can change the combo box selection programmatically from the view model; however, when the user makes a selection from the interface (view), the view model variable is not updated.
Here is the XAML (snipet):
<ComboBox Width="100"
HorizontalContentAlignment="Center"
SelectedIndex="{Binding GroupSortIndex,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
<ComboBoxItem Content="Appearance"/>
<ComboBoxItem Content="Created"/>
<ComboBoxItem Content="Name"/>
</ComboBox>
Here is a portion of the view model:
public int GroupSortIndex
{
get { return (int)GetValue(GroupSortIndexProperty); }
set { SetValue(GroupSortIndexProperty, value); }
}
public static readonly DependencyProperty GroupSortIndexProperty =
DependencyProperty.Register("GroupSortIndex", typeof(int),
typeof(MainWindowViewModel), new UIPropertyMetadata(-1));
What do I need to do to have GroupSortIndex updated when the user makes a selection from the IU?
yes, The SelectedIndex property looks has a bug. i was this problem and i solved it by changing the SelectedIndex to SelectedItem.
Change XAML SelectedIndex to SelectedItem:
<ComboBox ItemsSource="{Binding Path=YourOptionsList}"
SelectedItem="{Binding SelectedOption}" />
Somewhere you must set the DataContext of your Window to reference the collection from your XAML.
and in your ViewModel write this:
public List<String> YourOptionsList { get { return (int)GetValue(YourOptionsListProperty); } set { SetValue(YourOptionsListProperty, value); } }
public static readonly DependencyProperty YourOptionsListProperty = DependencyProperty.Register("YourOptionsList", typeof(List<String>), typeof(MainWindowViewModel), new UIPropertyMetadata(new List<String>()));
public string SelectedOption { get { return (int)GetValue(SelectedOptionProperty); } set { SetValue(SelectedOptionProperty, value); } }
public static readonly DependencyProperty SelectedOptionProperty = DependencyProperty.Register("SelectedOption", typeof(String), typeof(MainWindowViewModel), new UIPropertyMetadata("none");
public MainWindowViewModel()
{
YourOptionsList =new List<String>();
YourOptionsList.Add("Appearance");
YourOptionsList.Add("Created");
YourOptionsList.Add("Name");
}
I have a custom UserControl that contains a grid ...I wish to set the ItemsSource property of that grid by xaml code of of a data template in a resource dictionary...
then I have used dependency property... this is my implementation...
public partial class MyControlGrid : UserControl
{
// Dependency Property
public static readonly DependencyProperty MyItemSourceProperty =
DependencyProperty.Register("MyItemSource", typeof(ICollectionView),
typeof(MyControlGrid), new FrameworkPropertyMetadata(null, OnMyItemSourcePropertyChanged));
IDictionary<string, string> _columns = new Dictionary<string, string>();
private static void OnMyItemSourcePropertyChanged(DependencyObject obj,
DependencyPropertyChangedEventArgs args)
{
// When the color changes, set the icon color PlayButton
MyControlGrid muc = (MyControlGrid)obj;
ICollectionView value = (ICollectionView)args.NewValue;
if (value != null)
{
muc.MyGridControl.ItemsSource = value;
}
}
public ICollectionView MyItemSource
{
get
{
return (ICollectionView)GetValue(MyItemSourceProperty);
}
set
{
SetValue(MyItemSourceProperty, value);
//OnTargetPowerChanged(this, new DependencyPropertyChangedEventArgs(TargetPowerProperty, value, value));
// Old value irrelevant.
}
}
public MyControlGrid()
{
InitializeComponent();
}
}
this is the user control xaml code
<UserControl x:Class="GUI.Design.Templates.MyControlGrid"
Name="MyListControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WpfTkit="http://schemas.microsoft.com/wpf/2008/toolkit"
xmlns:Templates="clr-namespace:Emule.GUI.Design.Templates">
<StackPanel>
<WpfTkit:DataGrid ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Templates:MyControlGrid}}, Path=MyItemSource}"
x:Name="MyGridControl"
<StackPanel>
this is the binding path expression I use
<basic:MyControlGrid MyItemSource="{Binding MyDataContextVisibleCollection}"/>
this dont work and wpf output window dont show me any errors
note that, naturally, if I bind this directly in the user controls work fine
<WpfTkit:DataGrid ItemsSource="{Binding MyDataContextVisibleCollection}"
Waths I wrong?
thanks
p.s. sorry for my english
this
answer show me the way
use of PropertyChangedCallback work fine with my code:
public static readonly DependencyProperty MyItemSourceProperty =
DependencyProperty.Register("MyItemSource", typeof(IEnumerable),
typeof(MyControlGrid), new FrameworkPropertyMetadata(null,
new PropertyChangedCallback(MyControlGrid.OnItemsSourceChanged)));
alternatively I have to remove comment on OnTargetPowerChanged and fire the property changed event
set
{
SetValue(MyItemSourceProperty, value);
//OnTargetPowerChanged(this, new DependencyPropertyChangedEventArgs(TargetPowerProperty, value, value));
// Old value irrelevant.
}
correct with
public ICollectionView MyItemSource
{
get
{
return (ICollectionView)GetValue(MyItemSourceProperty);
}
set
{
SetValue(MyItemSourceProperty, value);
OnItemsSourceChanged(this, new DependencyPropertyChangedEventArgs(MyItemSourceProperty, value, value));
}
}