Binding to a datatemplate control WPF - wpf

I have a TabControl which binds an ObservableCollecion<T>. Every tab represents one object from OC. In the tab header i have a button:
<Button Command="{Binding DeleteCommand}">x</Button>
which should delete an object from the observable collection and consequently represent the result as the deleted tab. However the command is not acknowledged when i click on the x button, probably because it searches for a property in its model(?). Is there any way this would work?

You can bind to TabControl's DataContext using RelativeSource markup extension.
Also, in case you want to remove the item from ObservableCollection, pass the binding via CommandParameter which will be an instance of T (model object).
<Button Command="{Binding DataContext.TestCommand,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=TabControl}}"
CommandParameter="{Binding}"/>

Related

WPF: why my ComboBox Selected Value Command return wrong value [duplicate]

I'm using MVVM and custom ICommand objects are provided by ViewModel layer. One ViewModel object at the same time can be attached via DataContext property to many View objects (windows, pages, etc). In ICommand.CanExecute() I want to check absence of validation errors for some controls in View (which attached to ViewModel props, significant for a particular VM command). One ViewModel can provide many commands, each of them has own set of controls for errors validation verification. So, pseudo-XAML is:
<Button.CommandParameter>
<x:Array Type="sys_win:DependencyObject">
<sys_win:DependencyObject>
<reference_to_textbox_or_other_control/>
</sys_win:DependencyObject>
<sys_win:DependencyObject>
<reference_to_textbox_or_other_control/>
</sys_win:DependencyObject>
</x:Array>
</Button.CommandParameter>
The second problem is that the particular command may be invoked by control, which itself is the part of the DataTemplate for collection item (in my case - part of ListBoxItem data template). My templated listbox item has two text boxes (binded to two props of corresponding ViewModel) and button, which invoke the ViewModel command. So, in command CanExecute() I need to check for validation errors for some window controls & two text boxes, which belongs to this listitem, not other items. The code below works fine if I want to pass ListBoxItem.IsSelected property as CommandParameter:
<Button DataContext="{Binding}"
Command="{Binding Path=SwitchCommand}"
CommandParameter="{Binding Path=IsSelected, RelativeSource={
RelativeSource
Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}}}"/>
But how can I pass whole (DependencyObject)ListBoxItem as the CommandParameter? And how this ListBoxItem, passed via {Binding RelativeSource} can be mixed with other current window controls in the first code example?
I'm very sorry, but how can I add the references to controls in xaml?
<Button.CommandParameter>
<x:Array Type="sys_win:DependencyObject">
<sys_win:DependencyObject>
<reference_to_textbox_or_other_control/>
</sys_win:DependencyObject>
<sys_win:DependencyObject>
<reference_to_textbox_or_other_control/>
</sys_win:DependencyObject>
</x:Array>
</Button.CommandParameter>
Just use a binding with no Path :
<Button DataContext="{Binding}"
Command="{Binding Path=SwitchCommand}"
CommandParameter="{Binding RelativeSource=
{RelativeSource
Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}}}"/>
I'm not sure if I'm reading your example correctly, but it seems to violate a bit of the MVVM principle. (My apologies if I read it incorrectly).
The idea behind MVVM is to decouple the viewmodel from any dependency on a XAML / View entity. You're breaking that by having the CommandParameter dependent on the usercontrol. What I would do is create state properties in the ViewModel and bind the usercontrol validations to those states, then in CanExecute you can test the values of those properties rather than trying to bind to a usercontrol.

listview checkbox binding wpf MVVM

I am trying to get the bool value of the checkbox present in the Listview. I am binding to a bool public property "Assignm" in the view model. I tried the below binding pattern but the problem is if i select one checkbox it selects all checkboxes and vice versa. I think this is because relativesource is listview and it works on complete listview. I also tried changing the relative source to ListviewItem but that didn't trigger anything. Can someone help me please. Do i need to change something here ?
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox Tag="{Binding MU_Identifier}" IsChecked="{Binding DataContext.Assignm, RelativeSource={RelativeSource FindAncestor, AncestorType=ListView}}">
</CheckBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
Because your binding for IsChecked property is Assignm property which seems to be one property of your view model.
If there is a boolean property named Assignm for the data model of the DataSource of ListView, then just change the binding like this: {Binding Assignm}, as Tag property does.
All your items are bounded to a single property, so when one item changes a property in your context it changes on other items.
To provide correct work all your items from ItemsSource should have property IsChecked.
Check this Example

Binding MenuItem's IsChecked to TabItem's IsSelected with dynamic tabs

I have a list of tab items that have views dynamically added to them. Every time a user adds a view, a new tab item is created. I'm now trying to bind a menu to a tabcontrol's items so that a user can select from a menu which view is currently the active view.
My menu is bound as such:
<Menu Background="Transparent">
<MenuItem Style="{StaticResource TabMenuButtonStyle}" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabControl}}, Path=Items}" ItemContainerStyle="{StaticResource TabMenuItem}"></MenuItem>
</Menu>
This works fine and has the desired effect (each menu item is a listing of all the open tabs).
I have the following style that binds menu items to the IsSelected property of the tab items:
<Setter Property="IsChecked" Value="{Binding Path=IsSelected, Mode=TwoWay}" />
My problem is, this binding doesn't work. The binding error message is stating that it can't find the IsSelected property on the view object. I don't want it to use the specfic view, rather, I want it to look at the tab item that the view is currently bound to.
I've tried the following, but still get a binding error:
<Setter Property="IsChecked" Value="{Binding Path=IsSelected, Mode=TwoWay, RelativeSource={RelativeSource AncestorType=TabItem}}}" />
Which states that it can't find an ancestor of type TabItem for each menu item (which makes sense as the menu item's ancestors are not what it is bound to.)
Is there any way I can get access to the parent of the item that is coming in as a binding so I can bind to its properties?
Update:
Per Yadyn's advice, I decided to create a value converter and return tab items.
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
ItemCollection ic = (ItemCollection)value;
List<TabItem> tabItems = new List<TabItem>();
foreach (var obj in ic) {
tabItems.Add((TabItem)obj);
}
return tabItems;
}
This makes binding IsSelected to IsChecked work for static items (TabControls that have their tab items already created), but for the dynamically added views, the Convert method never gets called. It's like the TabControl is not sending out an update to binders of its items that something has changed. Here is how the MenuItem is wired up now:
<MenuItem Style="{StaticResource TabMenuButtonStyle}" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabControl}}, Path=Items, Mode=OneWay, NotifyOnSourceUpdated=True, Converter={StaticResource TabControlItemConverter}}" ItemContainerStyle="{StaticResource TabMenuItem}"></MenuItem>
TabControl.Items will get you back the views, since that is what you've bound to your TabControl to have dynamic tab views.
Unfortunately, there isn't a property you can bind to on the TabControl directly that will get you a collection of the TabItems. These are actually the ItemContainers for each item in the Items bound collection.
What you might do is create a converter or something. You can try using myTabControl.ItemContainerGenerator.ContainerFromItem and pass in the view object to get back the actual TabItem that wraps it. Then your IsSelected binding will work.
You might consider binding directly to the TabControl itself instead of the Items property. Then the converter can easily do the above call to ContainerFromItem. You'll then have to return a List<TabItems> from the converter by enumerating the Items property yourself (calling ContainerFromItem for each).
Anyway, hopefully this gets you on the right track!
Here is something simpler. Define a toplevel viewmodel that holds a collection of viewmodels representing the tabs and menuitems like so
//Not showing here the details of implementing INPC
public class MyCustomCompositeViewModel:INotifyPropertyChanged
{
public ObservableCollection<CompositeViewItem>CompositeItems{get;set;}
public CompositeViewItem SelectedItem{get;set;}
}
On the view, bind the tabitems to the CompositeItems collection and bind the selected tab item to the selectedItem. You can bind the MenuItems similarly
The compositeviewitem should provide properties like the name of the item (for display on the tab and menu), and perhaps the additional data the View needs for rendering. Hope this makes sense.

Setting RelativeSource correctly

I have a combo box on a xaml form (MainWindow).
I set the Items source to an ObservableCollection in the code behind. To populate the Combo box I used Relative Source (it sits inside an ItemsControl), which worked great (without it, if did not populate):
ItemsSource="{Binding SelectableItems, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
I have now factored out the ObservableCollection into a seperate View Model Class, named 'MainWindowViewModel', the combo box does not populate.
I have set the DataContext of the MainWindow to my ViewModel and have checked that it populates other controls as expected, which it does.
How should I construct the RelativeSource so the combo box populates?
Thanks
Joe
I needed to add the Path at the end, thus:
ItemsSource="{Binding SelectableItems, RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=DataContext.SelectableItems}"
You do not want to use a RelativeSource any longer. If you don't specify a RelativeSource (or Source, or ElementName), then the binding will resolve against the current DataContext. Since the DataContext is inherited, your ItemsControl obtains its DataContext from the parent Window. Thus, this binding will resolve against your view model.
ItemsSource="{Binding SelectableItems}"

How to bind to a property of the ViewModel from within a GridView

I'm using the MVVM design pattern, and the DataContext of my View is set to a ViewModel.
In my View, I have a ListView/GridView with ItemsSource bound to a DataTable. One of the GridViewColumns has a CellTemplate that presents a Button. I want the IsEnabled property of the button to be bound to the SelectButtonsEnabled property of my ViewModel.
Using IsEnabled="{Binding Path=SelectButtonsEnabled}" doesn't work because my DataTable doesn't have a column named "SelectButtonsEnabled". The property I'm trying to bind to is global for the whole ViewModel, not specific to a row of the DataTable.
I think I need some kind of RelativeSource tag, but all my attempts thus far have failed.
Thanks.
Well, I don't know if this is the most succinct way to do it, but this works:
IsEnabled="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.SelectButtonsEnabled}"

Resources