WPF Command Binding to ancestor property - c#

I have a doubt about binding to an ancestor property in WPF. My situation is: my window has a view model as data context with all the commands as ICommand properties. I have a list of checkboxes as follows:
<ItemsControl ItemsSource="{Binding CurrentCustomer.SuppiersSelection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox FontSize="16" Content="{Binding Path=Supplier.Company}"
IsChecked="{Binding Path=Selected}"></CheckBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The question is: I want that each checkbox has a command bound to it, so that when a supplier is checked or unchecked the relationship between customer and supplier is automatically saved. My problem is that the command properties are on the Window Data Context and this checkboxes is using as data context the ItemsSource of ItemControl. How do I bind to the property on the Window Data Context?
Thanks in advance for your help.

{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=DataContext}
But it's not clear what is the task.
Why don't you use two way binding and process the change of property Selected?

Related

WPF: binding through custom dependency property

I have an TabControl bound to a Dictionary and has a custom control as it's ContentTemplate. The custom control has a custom dependency property Schedules and it's DataContext is bound to a ViewModel, Here is how it look like:
Main control:
<TabControl Grid.Row="1" ItemsSource="{Binding Schedules}">
<TabControl.ContentTemplate>
<DataTemplate>
<TabControl >
<TabItem Header="Scheduled flights">
<views:MyViewer Schedules="{Binding Value}"/>
</TabItem>
</TabControl>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
In MyViewer, I also have an DataGrid that I want it to be bound the Schedules passed from the TabControl, but in the same time MyViewer has a ViewModel assigned to it. This is how it looks like in MyViewer:
<DataGrid Grid.Row="1" ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=Schedules}" AutoGenerateColumns="False" >
So far this is not working, when MyViewer is loaded Schedules property is null. But even if it works, I would want the Schedules to be passed to the ViewModel not code behind. One idea is to populate the Dictionary with ViewModels of MyViewer, but I do not wish to do this, I only want the Main control to know about details of MyViewer. So any clean idea to solve this?
EDIT:
The proposition above does work after changing to ObservableDictionary, but the question remains, how to have the Schedules in the ViewModel
If MyViewer has it's own ViewModel, you should not rather do hacks like this:
ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=UserControl}, Path=Schedules}"
This way MyViewer is tightly coupled to some other control.
Im my opinion, in this case, MyViewer's ViewModel should have it's own property Schedules. How could you keep TabControl ViewModel's Schedules and MyViewer ViewModel's Schedules in sync? It depends on your system, but you could try with this ideas:
Sending ViewModel level messages like in MVVM Light, when adding or removing items. Example in this blog post
Try to implement some kind of store like in NgXs or NgRx in Angular
Maybe you don't need to keep Schedules in sync - depends on your system? :)

DisconnectedItem in .NET 4.5.1

I have been searching for the DisconnectedItem issue over the Internet and I have learned that it should have been a solved problem in 4.5 release of .NET Framework. I am currently using 4.5.1 and it's my first time to face this kind of malfunction. The scenario is I have a ViewModel and a View. After an entity changed event (nHibernate) I dispose the old ViewModel, create a new one of the same type and attach it to already existing View (resolved form the Unity container). The problem is that the View has an ItemsControl with ItemsSource bound to the ViewModel List. After attaching the new ViewModel, the DelegateCommand refreshes the CanExecute methods. At first I get the objects, but than comes a sequence of DisconnectedItems (so even though I return false if DisconnectedItem, I'd get all buttons blocked). Is there any other way of changing the DataContext without creating a brand new View?
Here is the "after entity changed" part. The reportFilesCollection is bound to ItemsControl:
_detailsViewModel.Dispose();
_detailsViewModel = new DetailsViewModel(reportFilesCollection, _unityContainer);
IDetailsView view = GetViewOfTypeFromRegion<IDetailsView>();
view.ViewModel = _detailsPreviewViewModel;
And the View part:
<StackPanel x:Name="reportDataPart">
<ItemsControl ItemsSource="{Binding ReportFiles}"
Style="{StaticResource IconDataTableStyle}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl Style="{StaticResource IconDataStyle}"
Tag="PrinterPath">
<Label ToolTip="Report generation time"
Content="{Binding CreatedOn, StringFormat={}{0:g}}"/>
<Button Content="Sign report"
Command="{Binding DataContext.SignReportCommand, ElementName=reportDataPart}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=DataContext}"/>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
The second ItemsControl (the inner one) provides a special style for the row of data. The absence of it doesn't make any difference. After I reenter the View, all of the buttons are active again but DisconnectedItems are still being present.

Getting Checked property from CheckBox when Command is called in WPF

I have a grid of CheckBoxes in a WPF C# project. Each CheckBox's Command property is bound to a CheckBoxChangedCommand in my WView.xaml file, like so:
<CheckBox Grid.Row="0" IsChecked="true" x:Name ="CheckBox0"
Command="{Binding CheckBoxChangedCommand}" />
<CheckBox Grid.Row="1" IsChecked="true" x:Name="CheckBox1"
Command="{Binding CheckBoxChangedCommand}" />
Each time one of the CheckBoxes is either checked or unchecked, I call CheckBoxChanged. How would I go about displaying a pop-up window showing either 1. the row number in the grid of the CheckBox and the name of the CheckBox ("CheckBox0", for example) and 2. The Checked value (true/false) for the checkbox?
My CheckBoxChangedCommand, in WViewModel.cs file, looks like this:
public ICommand CheckBoxChangedCommand
{
get
{
return new RelayCommand(param =>
{
MessageBox.Show("CheckBoxChanged!");
});
}
}
How can I access the IsChecked property and the row number of the CheckBox that triggered CheckBoxChanged from withinCheckBoxChanged? How can I pass the data from my View to my ViewModel?
You definitely need to do more with binding here.
First of all, you should probably be binding the IsChecked property of your Checkboxes to a property on your viewmodel.
Second, based on your comment about needing to know the row number of the checkbox that was checked, I'd say you probably need to be generating the "row" including the CheckBox via databinding, so that you can then pass the object that represents the row as the CommandParameter to your CheckBoxChangedCommand.
So something like:
<ListBox ItemsSource="{Binding MyItems}" />
and then in your resources:
<DataTemplate DataType="{x:Type local:MyItemType}">
<CheckBox IsChecked="{Binding IsChecked}"
Command="{Binding CheckChangedCommand}"
CommandParameter="{Binding}" />
</DataTemplate>
Note: Your CheckChangedCommand is probably on the main ViewModel, not the item-level ViewModel, so you probably need to do some other type of lookup to make it find it - this example is just for simplicity

Binding beyond the DataContext

I have a DataTemplate for templating my ItemsControl's items which are TimeSheet's Details.
I have couple of TextBox representing certain values of my TimeSheet's Details but their IsEnabled property depends on the TimeSheet itself, not the details.
<ItemsControl
ItemsSource="{Binding Path=TimeSheet.TimeSheetDetails}"
ItemTemplate="{StaticResource TimeSheetDetail}"
/>
<DataTemplate x:Key="TimeSheetDetail">
<TextBox
Text="{Binding Houre}"
IsEnabled="Binding ??????">
</DataTemplate>
Since the IsEnabled property cant be found in the TimeSheetDetails but can be found in my ViewModel, i would like to bind directly to my ViewModel's Property but when i try binding, to my ViewModel from my DataTemplate, it only seems to look in my TimeSheetDetail.
How can i access my ViewModel's public property directly?
You can bind to your parent's DataContext:
{Binding DataContext.IsEnabled,
RelativeSource={RelativeSource FindAncestor, AncestorType=ItemsControl}}

MVC design issue

I'm having an application using MVC. It has a canvas and property grid. When an item is selected in the canvas. The property grid should display its details.
So I made an event listener and when item is selected in the canvas it raises an event to the controller which pass the selected item to the property grid to display the details.
Model :
Item object containing name, description
Controller :
protected Controller(object model, FrameworkElement view)
{
this._model = model;
this._view = view;
}
public virtual void Initialize()
{
View.DataContext = Model;
}
View :
<TextBlock>Status</TextBlock>
<ComboBox ItemsSource="?????"/>
Where view is the property grid and model is the selected item.
The problem is in the property grid there is a dropdown list containing lookup values how can I get the dropdown values given that the datacontext of the property grid has already been set to the selected item which doesn't contain reference to these lookup items.
I know that it's easy to use custom code to do that. But I don't want to violate the MVC aproach.
Bind to a source rather than DataContext, sources are provided by ElementName, RelativeSource & Source, so you can name the View for example and use ElementName to get it as source then the Path could be DataContext.LookupValues or whatever your property in the model (- the DataContext of the View is your model -) is called.
e.g.
<Window ...
Name="Window">
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" />
<ComboBox ItemsSource="{Binding ElementName=Window, Path=DataContext.Occupations}"
SelectedItem="{Binding Occupation}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- ... --->
Edit: Your problem seems to be that you do not pass the information you need, consider a design which still grants you access to more than just the SelectedItem of some list, e.g.
<Window ...
Name="Window">
<ListBox Name="listBox" ItemsSource="{Binding Data}" />
<ContentControl DataContext="{Binding ElementName=listBox, Path=SelectedItem}">
<ComboBox ItemsSource="{Binding ElementName=Window, Path=DataContext.Occupations}"
SelectedItem="{Binding Occupation}" />
</ContentControl>
<!-- ... --->
The DataContext of the ContentControl may be the SelectedItem of the ListBox but the ComboBox inside can still reference the DataContext of the Window which should provide the necessary information.
This is similar to my first example in that the DataContext inside the DataTemplate is always an item of the collection but you can access external DataContexts using sources in your bindings.

Categories

Resources