SL ItemsControl, command on ViewModel not firing from ItemsControl (CheckBox) - silverlight

I'm using PRISM v2, CAL, SL4 and MVVM and have a delegate command on my ViewModel called CheckCommand. The ItemsControl contains a checkbox and I'm trying to get the items in ItemsControl/Checkbox to fire this command when it's checked - but it's not communication back to the viewmodel!
I think it's because each items 'datacontext' is the individual object the item is bound to, rather than the ViewModel?
- My suspicion is actually correct, cause if I move my DelegateCommand out of the viewmodel and into the class defining the items in itemscontrol I can see the commands/methods beeing fired!
View:
<ListBox x:Name="BasketListBox" ItemsSource="{Binding BasketCollection}" MinWidth="200">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox commands:Checked.Command="{Binding CheckCommand}" IsChecked="False" </CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
Can anyone point me in the right direction please?
Cheers, Mcad.
EDIT 1:
The commanding now works, see solution below. BUT, I now run into another problem:
"An exception occurred while creating a region with name 'basketRegion'. The exception was: System.InvalidOperationException: ItemsControl's ItemsSource property is not empty. This control is being associated with a region, but the control is already bound to something else. If you did not explicitly set the control's ItemSource property, this exception may be caused by a change in the value of the inherited RegionManager attached property"
Created seperate question for this problem to make it more clean:
PRISM-MVVM, ItemsControl problem with View injection

You want every CheckBox to fire the same command? You could:
<CheckBox commands:Checked.Command="{Binding DataContext.CheckCommand, ElementName=BasketListBox}"
Or you could have every child view model expose the command via their own property.

Thanx Kent. You put me on the right path to solve this, ended up doing this:
<ListBox x:Name="basketListBox" ItemsSource="{Binding basketcollection}" MinWidth="200">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox commands:Checked1.Command="{Binding DataContext.CheckCommand, ElementName=basketListBox}" Content="{Binding basketName}"> </CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>

Related

DataGridColumnHeader of a DataGrid defined in a DataTemplate for a ViewModel

I have successfully used ProxyElement to pass Data Context of my Data Grid to DataGridColumnHeaders. However, I am trying out something new and I just can't figure out what I am doing wrong over here.
Here is what I am trying to do: I am creating a UserControl and associating it to my ViewModel in my Resources file (see Resources.xaml code snippet below).
Resources.xaml:
<ResourceDictionary
xmlns:myVm="clr-namespace:..."
xmlns:myUserControl="clr-namespace:...">
<DataTemplate DataType={x:Type myVm:DummyModel}">
<myUserControl:DummyUserControl />
</DataTemplate>
</ResourceDictionary>
Now in my UserControl, I have a DataGrid with a DataGridComboBoxColumn. I am trying to access my data context to set its item source and in the past I was able to do it using proxy element. This time however I am not able to do so. (See DummyUserControl.xaml code snippet below)
DummyUserControl.xaml:
<UserControl x:Class="Client.MyControl.DummyUserControl"
...>
<UserControl.Resources>
<FrameworkElement x:Key="ProxyElement" x:Name="ProxyElement"
DataContext="{Binding}" />
</UserControl.Resources>
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Products}">
<DataGridComboBoxColumn
Header="Company"
ItemsSource="{Binding Path=DataContext.ProductCompanies,
Source={StaticResource ProxyElement}}"
DisplayMemberPath="Name" SelectedValuePath="Id"
SelectedValueBinding="{Binding CompanyId}" />
</DataGrid>
</UserControl>
When I do this, my binding fails with the following message:
System.Windows.Data Error: 3 : Cannot find element that provides DataContext.
BindingExpression:(no path); DataItem=null; target element is 'FrameworkElement'
(Name='ProxyElement'); target property is 'DataContext' (type 'Object')
I have no idea what to do here. I remember reading that the datacontext for a datatemplate is automatically set, so I have no idea why the data context is null in this case. To prove it is null, I also tried setting the binding in the code-behind file and added a breakpoint to check its value (which was null).
Can anyone suggest what to do here?
Edit 1
I have also tried the following approaches:
Remove ProxyElement altogether and see if it can detect DataContext. To no surprise, this failed.
Tried binding to the templated parent. Fail.
Tried binding to the UserControl itself. Fail.
I also tried referencing the data context of the parent item where this view model is going to be displayed, which is in a TabItem of a TabControl.
All of the alternate bindings gave me same error as the error above.
Here is a (working but not preferred) solution to this problem. You will realize why it is not preferred by the end of it.
The key to this problem is understanding what and how data context of a data template works. Whenever you define a Data Template for a View Model, the data context for the view that follows, whether it is a user control or just xaml itself, is the View Model! This shouldn't be a surprise to anyone.
But this will surprise people: if you specify a User Control, the Data Context of the User Control is not set during construction of the User Control! In other words, in the constructor of User Control, Data Context is going to be null. Furthermore, any XAML code that relies on the Data Context at construction time, which in this case was my FrameworkElement resource called ProxyElement got its DataContext set to null because it gets constructed at construction time of the User Control!
So when does the DataContext get set? Simply after the User Control is created. In pseudo code, this following describes the logic behind drawing a ViewModel:
Draw ViewModel x;
DataTemplate in ResourceDictionary says ViewModel x can be drawn using UserControl abc
Let's create a new instance of UserControl abc
Let's now assign the DataContext of abc to the ViewModel itself.
Let's return the newly created instance of UserControl abc
So what do we do to solve the problem in this question?
UserControl.xaml:
<UserControl ...
DataContextChanged="DaCoHasChanged">
<UserControl.Resources>
<FrameworkElement x:Key="ProxyElement" /> <!--Remove DataContext="{Binding}"-->
</UserControl.Resources>
<DataGrid ...>
<DataGridComboBoxColumn
ItemsSource="{Binding Path=DataContext.ProductCompanies,
Source={StaticSource ProxyElement}}"
... />
</DataGrid>
</UserControl>
UserControl.xaml.cs:
private void DaCoHasChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var proxyElement = Resources["ProxyElement"] as FrameworkElement;
proxyElement.DataContext = e.NewValue; // instead of e.NewValue, you could
// also say this.DataContext
}
I am trying to figure out a way of getting rid of the code in the code-behind file. But till then, if someone else hits this problem, then they might be able to get inspired from this solution.
Credit to the concept behind this solution goes to: How to set the DataContext for a View created in DataTemplate from ViewModel
Try this
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Products}">
<DataGridTemplateColumn Header="Company">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox
ItemsSource="{Binding Path="{Binding DataContext.ProductCompanies,RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
DisplayMemberPath="Name" SelectedValuePath="Id"
SelectedValueBinding="{Binding CompanyId}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Now as I got your problem I think the problem is in DataGridComboBoxColumn I dont know why it not Binding using RelativeResource . Try it with DataGridTemplateColumn and you would not require any ProxyElement I hope this will help.

WPF Create user control dynamically on change of observablecollection OR Visualize nested ObservableCollections by using nested User controls

I am new to WPF, and I can't find a solution to my problem.
I've got an Observable Collection in my ViewModel:
ObservableCollection<Process>
Within the Process class is another ObervableCollection:
ObservableCollection<BurstTime>
I want to add a new User control (what can vizualize the actual state of Process data, and it's burst times) dynamically to one of my StackPanel in my view each time, when a new Process is activated, and remove it when the process is ended (when the observable collection changes).. During it's run, I want to diplay it's Burst Time collection, what can change in time..
I've tried to subscribe to the CollectionChanged event in my user control's codebehind and use a User Control inside another User Control and dynamicalli create required TextBlocks when my event handler runs, but I always get a System.Reflection.TargetInvocationException (inner exception: System.NullReferenceException) when my outer InitializeComponent() is running..
public ResourceUseView()
{
InitializeComponent();
((ObservableCollection<Process>)DataContext).CollectionChanged += ResourceUseView_CollectionChanged;
}
Is it possible to do it? Is it possible to create or remove User controls in my outer UserControl's codebehind while reacting to ObservableCollection's element changes, and update the outer when an element changes in the inner ObservableCollection? Are there other ways than instantiate a UserControl inside another User Control dynamically? Is there any special control, what can display ObservableCollection in ObservableCollection?
(If it'is possible, I would like to have different user controls as much as elements my ObservableCollection have..)
Thanks for your help!
The constructor runs before the data binding, so you are probably seeing that error because the DataContext is null at the time the constructor runes.
To bind to a collection, use a control that contains an ItemsSource property. And if you want to bind to a nested collection, modify the ItemTemplate of the control to use another control with an ItemsSource property for the nested collection.
Here's an example using an ItemsControl
<ItemsControl ItemsSource="{Binding Processes}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <ItemsControl ItemsSource="{Binding BurstTimes}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>
I have some examples of the ItemsControl on my blog if you're interested.
If you want to specify a UserControl for each object, then set the ItemTemplate for the first ItemsControl to be the ProcessUserControl, and inside the ProcessUserControl have an ItemsControl bound to BurstTimes and use a BurstTimeUserControl for it's ItemTemplate
<ItemsControl ItemsSource="{Binding Processes}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:ProcessUserControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
...
<UserControl x:Class="MyNamespace.ProcessUserControl ...>
...
<ItemsControl ItemsSource="{Binding BurstTimes}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:BurstTimeUserControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
...
</UserControl>

Adding resource in code and using it as StaticResource

For some reason I am unable to bind to ViewModel properties within DataTemplates on some controls. The result of the binding itself is unpredictable, sometimes it work, sometimes it doesn't. For this reason I am thinking of exposing the ViewModel in some other way besides setting it as DataContext.
First thought was to add ViewModel to Resources collection. I am using TabControls for UI, so whenever a view needs to be displayed, it is done through Data templates.
<DataTemplate DataType="{x:Type vm:SomeViewModel}">
<vw:SomeView />
</DataTemplate>
In this situation the view is instantiated automatically, and its DataContext is set to ViewModel set in template. Is there a way I can make this ViewModel available to View's Resources (ex with key=viewModel), so that I can use it like this:
<TextBlock Text="{Binding SomeProperty, Source={StaticResource viewModel}}" />
I have tried adding it in code, in the Loaded event for the View:
this.Loaded += (s, e) =>
{
this.Resources.Add("viewModel", this.DataContext);
};
Above code is executed before the error pops up that says static resource is not found at run-time, so the resource was added to collection.
Any ideas what can I do?
You can define ViewModel as a resource in XAML like that:
<vm:SomeViewModel x:Key="ViewModel"/>
If you want to Bind to DataContext in a DataTemplate you can use the following:
{Binding Path=DataContext, ElementName=uc}
assuming that your window/usercontrol name is x:Name="uc" , or as #stukselbax wrote:
{Binding Path=DataContext, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=[UserControl|Window]}}

How do I bind a generic window to an arbitrary view model at runtime, using DataTemplates?

I have a large number of ViewModel classes. For each of these classes, there is a corresponding .xaml file which is a 'UserControl'. In my App.xaml, I have them registered as DataTemplates, like so:
<DataTemplate DataType="{x:Type viewModel:MainMenuViewModel}">
<view:MainMenuView/>
</DataTemplate>
With the idea being that WPF will be able automatically swap in the necessary user controls at runtime. For example, this works:
<Grid>
<StackPanel>
<TextBlock Text="SuperApp" />
<ItemsControl>
<ViewModels:MainMenuViewModel/>
</ItemsControl>
</StackPanel>
</Grid>
In that the entry "MainMenuViewModel" is automatically replaced by the MainMenuView, bound to the MainMenuViewModel. Great. My current goal is now this: I want to have a button, on, say, a view embedded in the MainMenuView, which opens a popup window, which will have a new ViewModel inside. The idea is to set it up so that I have a single 'generic' popup form, in which I embed an arbitrary ViewModel, and let WPF handle actually rendering it with DataTemplates, similar to the above. So I have a command bound to a button, like so:
<Button Command="{Binding Path=LaunchInStandaloneForm}" Content="Rip Out"/>
Which successfully creates a new window, sets the dataContext equal to the appropriate ViewModel, and shows the window.
The question is: How do I set up the XAML of this popup window so that it will render the appropriate DataTemplate for the ViewModel which is the DataContext? I've tried:
<Grid>
<ItemsControl ItemsSource="{Binding Path=.}">
</ItemsControl>
</Grid>
, but it comes up blank. Any pointers?
To set the ItemsSource to the DataContext, use ItemsSource={Binding}. That assumes that the DataContext is an enumerable collection of your View Model objects.
Updating with correct answer:
Use a ContentControl :)
Hope that helps.
The accepted answer here shows how to change templates at runtime. You should be able to dig out the answer from that. Any questions just shout.
How to modify silverlight combobox data display
Hope that helps

Silverlight 3: Using list of UserControls as ItemsSource for TreeView

I want to populate a TreeView with UserControls, but I only want the Name property to show up, not the entire UserControl. The following code gives me weird crashes as soon as I add something to myUCs:
C#:
var myUCs = new ObservableCollection<UserControl>();
MyTreeView.ItemsSource = myUCs;
XAML:
<controls:TreeView x:Name="MyTreeView">
<controls:TreeView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</controls:TreeView.ItemTemplate>
</controls:TreeView>
Does anyone know how to use a list of UserControls as an ItemSource for TreeViews?
I found one not so convenient workaround: instead of a List of UserControls, use a Dictionary, and change the XAML to:
<controls:TreeView x:Name="MyTreeView">
<controls:TreeView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key.Name}"/>
</DataTemplate>
</controls:TreeView.ItemTemplate>
</controls:TreeView>
The same bug(?) exists in ListBox, a solution is provided here:
Use UIElements as ItemsSource of ListBox in Silverlight
That particular fix does not work for TreeView
You may have to create your own class that extends UserControl and override the ToString() method so that it returns the name property.

Resources