Identifing context of clicked item from ItemsControl in code - wpf

I have some collection bound to ItemsControl. DataTemplate was customized to represent its elements. Now I have to perform some operation on data (one Cell from collection) corresponding to rendered DataTemplate in result of clicking it.
Window's DataContext is set to ViewModel containing my collection.
<DataTemplate x:Key="template"
DataType="logic:Cell">
<Ellipse .../>
</DataTemplate>
...
<ItemsControl ItemTemplate="{DynamicResource template}"
ItemsSource="{Binding collection}">
...
</ItemsControl>
I have just started with MVVM pattern for WPF. I'm looking for good pattern to deal with such issue (in MVVM style).

In your ViewModel, you can use the following snippet...
var v = CollectionViewSource.GetDefaultView(MyCollection);
var ci = v.CurrentItem;
int p = v.CurrentPosition;
where 'MyCollection' is the list you have bound to your ItemsControl. The variable 'ci' will contain an object which can be downcast to the item you need. The variable 'p' tells the index of where 'ci' is in your collection.
If 'MyCollection' inherits from IList, there are additional goodies you can access as well.
Adding: check for nulls before referencing

Related

WPF- Binding properties in a DataTemplate

I'm building a window with a set of rows that share the same layout, but their contents should be different, eg:
| (Label Content:)"Name1" | (Textbox Text)"SomeText" |
| (Label Content:)"Name5" | (Textbox Text)"OtherText" |
I've defined a DataTemplate which basically holds a Grid specifying the size of each column, holds all the elements it requires (a few labels, textboxes, etc.) and sets their common properties.
<UserControl.Resources>
<DataTemplate x:Key="AxisRangeEntry" x:Shared="False">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="50" />
....
</Grid.ColumnDefinitions>
<Label x:Name="MyLabel" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Center">
...
<TextBox x:Name="MyTextbox" Grid.Column="2" Width="110" HorizontalContentAlignment="Right" />
...
</Grid>
</DataTemplate>
</UserControl.Resources>
Then in my window I start adding the data template as ContentControls in a stack panel:
<ContentControl ContentTemplate="{StaticResource AxisRangeEntry}" />
<ContentControl ContentTemplate="{StaticResource AxisRangeEntry}" />
....
I'm struggling to figure out how I can define certain properties of controls inside the DataTemplate to be bindable to, and bind them to a static value/external property when I start defining the ContentControls. Effectively each ContentControl would need to be able to define things like it's MyLabel content and MyTextbox text.
I've previously created CustomControls, which had DependencyProperties on them, which I could then bind to when adding them on another window. With a DataTemplate however I'm not sure how I would define these fields as bindable and bind to them when including a new version of the template.
Any help would be appreciated.
From what it sounds like, you are not using the MVVM pattern.
For your situation, I'd recommend using MVVM -- take a look at this article for a quick intro for something that would fit your case (ItemsControl with an ItemTemplate)
What you would do is create an ObservableObject to represent each row, and then bind the collection of ObservableObjects to an ItemsControl's ItemsSource, with the ItemTemplate set to the DataTemplate you created. In the DataTemplate, you would specify each binding to the property on the ObservableObject's row, and WPF would bind to the correct instance for each row.
http://www.wpf-tutorial.com/list-controls/itemscontrol/
Either way, DataTemplates are primarily used for templating a certain data-type. If you really need to implement the view in this way, a custom UserControl with dependency properties would be the way to go.
You present a dynamic nature of items to be bound, so this answer will attempt to provide guidance within the parameter's set.
...[to] define certain properties of
controls inside the DataTemplate to be bindable to,
Within a template the binding will default to the parents data context. Simply saying {Binding} will default to that item in the data context. If the bound item has a specific property then use {Binding MyPropertyName}. Just verify that the parent, or its ancestors have a valid data context.
Think of data templates in its final location, as if you had hard coded it there. It will behave the same....
and bind them to a static value/external property when I start defining the
ContentControls.
Since this sounds like it is in a custom control, the datacontext will be the ultimate consumer's datacontext and most likely the datacontext will be worthless.
If it is on a custom control, then use named binding and bind it to a property on the control. For example the control's name, in XAML, is given the name "MyControl" (x:Name="MyControl")and in the template binding, one can path directly to it such as
{Binding MyCustomControlDependencyProperty, ElementName=MyControl}
created CustomControls, which had Dependency properties
With the above rules one can still, and should IMHO, use dependency properties of the custom control to pass on the information from the consumer to the the datatemplate which will use it dynamically..

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>

Set DataTemplate control property binding to the item from an ItemsSource collection?

I have an ItemsCountrol with its ItemsSource property bound to an ObservableCollection. I have a usercontrol (TeamUserControl) that displays this type. I created a datatemplate that loads this usercontrol for each customtype item in the itemssource collection. At this point any Binding statements I make inside TeamUserControl can reference Team properties directly by path {Binding Path=TeamOwner} and work. Is there a way to bind a reference to the ItemsSource item the usercontrol is representing? For example, in the TeamUserControl creating a dependancy property of type Team and have it bound to the instance of the item from the ItemsSource.
<ItemsControl Name="ItemCtrl" Grid.Row="0" ItemsSource="{Binding Path=League.Teams}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<mycontrols:TeamUserControl AttachedTeam="{Binding ???}" TeamOwnerName="{Binding Path=TeamOwner}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
In this example the window is representing a class "League" which has the property:
ObservableCollection Teams. And there is a class "Team" which has the property:TeamOwner. The TeamUserControl has two dependancy properties: AttachedTeam of type Team, and TeamOwnerName of type string.
I included the team-owner property reference to show that there is an instance of Team for each of these usercontrols. I am just not sure how to reference it.
As I understand you, you should to write
<mycontrols:TeamUserControl AttachedTeam="{Binding}" TeamOwnerName="{Binding Path=TeamOwner}"/>
{Binding} statement will bind to current item in ItemsSource, where type T is type which your ObservableCollection<T> League.Teams uses.
I also recommend you to read MSDN article about ItemsControl, and look around Binding to make it clear for yourself, what you can bind to.

MVVM ListBox controlling a Content Control

I've been going round in circles with this for a couple of days, and I'm hoping a WPF guru can see where I'm going wrong.
I'm setting CurrentViewModel in code. The Selected item of my ListBox and the Content of my ContentControl bind correctly. But when changing the selected item in the Listbox via the UI the CurrentViewModel is being set but the Content Control is not being updated.
I'm using a data template to map my Views and View Models.
<DataTemplate DataType="{x:Type ViewModel:MyViewModel}">
<View:MyView />
</DataTemplate>
I have a ListBox which is bound to an observable collection of ViewModels. The Selected Item is bound to the current view model.
<ListBox ItemsSource="{Binding MyViewModelCollection}" DisplayMemberPath="DisplayName" SelectedItem="{Binding CurrentViewModel, Mode=TwoWay}"/>
I also have a content control that is also bound to the CurrentView Model
<ContentControl Content="{Binding CurrentViewModel, Mode=TwoWay}"/>
This is the property that they are both bound to
public MyViewModel CurrentViewModel
{
get
{
return _currentViewModel;
}
set
{
if (_currentViewModel== value) return;
_currentViewModel= value;
OnPropertyChanged("CurrentViewModel");
}
}
I've edited the names for clarity and removed formatting information.
Any help greatly appreciated.
Cheers,
Daniel
EDIT: Came across the link How can I debug WPF bindings?. I set a break point on the Content binding and it does indeed only get called once when the binding is first set.
You should not be setting TwoWay as the mode on your ContentControl:
<ContentControl Content="{Binding CurrentViewModel, Mode=OneWay}"/>
This is because you intend your ContentControl to read the value, but never write it.
As an aside, you can also bind the ContentControl to the currently selected item in the collection, rather than to that property by doing this:
<ListBox ItemsSource="{Binding MyViewModelCollection}"
DisplayMemberPath="DisplayName"
IsSynchronizedWithCurrentItem="True"/>
<ContentControl Content="{Binding MyViewModelCollection/}"/>
The "slash" (/) at the end of the collection indicates the current item selected in the collection and setting that current item property is as simple as setting the IsSynchronizedWithCurrentItem equal to true.
A lot of times I find with this combination, I really don't need the extra property on my view model.
Anyway, I hope this helps.

WPF UserControl in DataTemplate within ItemsControl - how to bind to parent of ItemsSource

The subject line says it all really! I have a user control which can be bound successfully to, say, a Fullname object - i.e. it works ok.
I now need to show a list of these and, again, this works ok when the control is in a DataTemplate within ItemsControl.Template.
But, the control has a property (InEditMode) that is not a property of the Fullname object but of the object that has the FullnameList property to which the ItemsControl is bound, via ItemsSource. This InEditMode property works fine when the control is not in a list and is bound to parent sibling properties named, say, ParentInEditMode and ParentFullname.
The question is - what style of binding expression is required to 'get at' the edit mode property of the parent object when the control is an ItemsControl?
Or, should I re-design the Fullname object to contain an EditMode property?
Many thanks in advance!
Update:
The item (i.e. that which is in collection bound to the ItemsControl) does NOT have such a property. Code is very simple:
<ItemsControl ItemsSource="{Binding Path=FullnameList}">
...then...
<ItemsControl.ItemTemplate>
<DataTemplate>
<jasControls:NameView
NameValue="{Binding Path=.}"
InEditMode= ??????? />
The overall parent (the viewmodel for the window) has properties:
FullnameList
ParentInEditMode
Fullname (single item for testing NameView which works perfectly with this xaml outside of any list control using:
<jasControls:NameView NameValue="{Binding Path=Fullname}" InEditMode="{Binding Path=ParentInEditMode}"/>
I would like to apply the edit mode to the entire collection - making that flag part of Fullname does not seem right!?
I have found an answer to my own question, which I hope will help others.
The working syntax I have is this:
<StackPanel>
<ItemsControl ItemsSource="{Binding Path=FullnameList}">
...then...
<ItemsControl.ItemTemplate>
<DataTemplate>
<jasControls:NameView
NameValue="{Binding Path=.}"
InEditMode= "{Binding DataContext.ParentInEditMode,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StackPanel}}}" />
This correctly picks up the property that is a sibling of FullnameList and passes it to the data template item. More by luck than judgement, but I hope this is a valid way to do this!
For each Item in ItemsSource, ItemsControl creates the specified DataTemplate and to its DataContext it assigns the respective Item. Now every DataTemplate can bind to its item in its data context.
So I suppose your item does have a property "ParentInEditMode"; there should be no issue with binding to that property.
If it doesn't work, please update your question with some code.

Resources