I have a user control that has one dependency property. In my window I have a list of objects, and I am creating a uniform grid consisting of my user control. I am setting the ItemsSource to my list of objects, but I need to pass each respective object to the user control. Please see the code below - I need to pass in the Participant object to the LadderControl.
<ItemsControl Grid.Row="2" Name="Participants" ItemsSource="{Binding Path=MyEvent.Participants}">
// more code here, irrelevant
<ItemsControl.ItemTemplate>
<DataTemplate>
<ladder:LadderControl Participant="CURRENT_ITEM_IN_PARTICIPANTS_LIST"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Is there a way I can do this ? Should I be thinking about using a different pattern ?
Thanks
Just do the below, as the Participant is the context of each item
<ladder:LadderControl Participant="{Binding}"/>
You can simply access the DataContext Property in LadderControl to access the currrent participant.
There is no need for a separate dependency property.
class LadderControl
{
...
public IParticipant Participant
{
get{ return DataContext as IParticipant; }
}
...
One solution is to simply do:
<ladder:LadderControl Participant="{Binding Path=.}"/>
{Binding Path=.} should bind to the current element in the ItemsSource list.
Related
I wanted to know which tab is selected in a tab control. I am using MVVM.
I know that I can bind the SelectedIndex of the tab control, but I also can bind the selectedItem, that it is a TabItem object.
I have read that the view model shouldn't know about the view, and many times read that this means doesn't use objects of types that belongs to the view. So in this case, TabItem is an object that belongs to the view space, so I am wondering if it would be a good idea to bind to SelectedItem.
However, the advise to not bind types that belongs to the view, I understand more than that view model shouldn't to depend on the structure or the behaviour of the view, to separate both parts. However, from some point of view, TabItem is just an object, that I use in an isolate way (I am not tiding the view model to the view). I use tabItem as any other object.
So my question is, in MVVM is it really a bad idea to use a TabItem in my view model?
EDIT: I add the code of my tab control:
<TabControl HorizontalAlignment="Stretch" Margin="5,5,5,5" Grid.Column="0" Grid.Row="1" VerticalAlignment="Stretch" Grid.ColumnSpan="2">
<TabItem Header="Persons" Content="{Binding Persons}"/>
<TabItem Header="Cities" Content="{Binding Cities}"/>
I have a user control for persons and other user control for cities.
Yes, SelectedItem is definitely a good idea. On a TabControl, the SelectedItem will bind to the data type of the TabControls data source.
Example:
<TabControl x:Name="TabControl1"
Margin="10"
ItemsSource="{Binding Employees}"
SelectedItem="{Binding SelectedEmployee}">
</TabControl>
public class ViewModel
{
private Employee _selectedEmployee;
public IList<Employee> Employees { get; private set; }
public Employee SelectedEmployee
{
get { return _selectedEmployee; }
set
{
if (_selectedEmployee == value)
{
return;
}
_selectedEmployee = value;
OnNotifyPropertyChanged();
}
}
}
The ViewModel will not know anything about the UI data type, it only knows about the underlying data (the SelectedEmployee).
See: https://learn.microsoft.com/en-us/previous-versions/windows/silverlight/dotnet-windows-silverlight/cc672537(v%3Dvs.95)
I'm adding a close button to my tabs using the following guide:
http://www.codeproject.com/Articles/84213/How-to-add-a-Close-button-to-a-WPF-TabItem
This has become a problem because the event uses the 'parent' of the added tab to remove that tab from the tabcontrol. I'm binding the tab control using mvvm, so the parent property is apparently not being set and giving me a null reference exception for the parent when the event tries to remove from it.
Here's the binding so you get the idea:
<TabControl Name="tabControl" Margin="0,22,0.2,-5.2" ItemsSource="{Binding Tabs}" Background="#FF4C76B2"/>
Heres where the tabs are being added.
private void AddTab(object tabName)
{
ClosableTab newTab = new ClosableTab();
newTab.Title = "title?";
//newTab.Header = tabName;
TextBox test = new TextBox();
test.Text = "CONTENT (" + tabName + ") GOES HERE";
newTab.Content = test;
Tabs.Add(newTab);
OnPropertyChanged("Tabs");
}
Here is the event where the null reference is taking place:
void button_close_Click(object sender, RoutedEventArgs e)
{
((TabControl)this.Parent).Items.Remove(this);
}
As I see it there are two options:
try to find another way to remove the tab (without the parent
property)
try to find a way to somehow set the parent property (which cant be
done directly, it throws a compiler error)
That doesn't sound like MVVM to me. We work with data, not UI elements. We work with collections of classes that contain all of the properties required to fulfil some requirement and data bind those properties to the UI controls in DataTemplates. In this way, we add UI controls by adding data items into these collections and let the wonderful WPF templating system take care of the UI.
For example, you have a TabControl that we want to add or remove TabItems from... in a proper MVVM way. First, we need a collection of items that can represent each TabItem:
public static DependencyProperty ItemsProperty = DependencyProperty.Register("Items", typeof(ObservableCollection<string>), typeof(TestView));
public ObservableCollection<string> Items
{
get { return (ObservableCollection<string>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
I'm just using a DependencyProperty because I knocked this up in a UserControl and I'm just using a collection of strings for simplicity. You'll need to create a class that contains all of the data required for the whole TabItem content. Next, let's see the TabControl:
<TabControl ItemsSource="{Binding Items}" ItemTemplate="{StaticResource ItemTemplate}" />
We data bind the collection to the TabControl.ItemsSource property and we set the TabControl.ItemTemplate to a Resource named ItemTemplate. Let's see that now:
xmlns:System="clr-namespace:System;assembly=mscorlib"
...
<DataTemplate x:Key="ItemTemplate" DataType="{x:Type System:String}">
<TabItem Header="{Binding}" />
</DataTemplate>
This DataTemplate defines what each item in our collection will look like. For simplicity's sake, our strings are just data bound to the TabItem.Header property. This means that for each item we add into the collection, we'll now get a new TabItem with its Header property set to the value of the string:
Items.Add("Tab 1");
Items.Add("Tab 2");
Items.Add("Tab 3");
Note that I included the System XML Namespace Prefix for completeness, but you won't need that because your DataType will be your own custom class. You'll need more DataTemplates too. For example, if your custom class had a Header property and a Content property, which was another custom class, let's say called Content, that contained all of the properties for the TabItem.Content property, you could do this:
<DataTemplate x:Key="ItemTemplate" DataType="{x:Type YourPrefix:YourClass}">
<TabItem Header="{Binding Header}" Content="{Binding Content}" />
</DataTemplate>
<DataTemplate DataType="{x:Type YourPrefix:Content}">
<YourPrefix:SomeUserControl DataContext="{Binding}" />
</DataTemplate>
So this would give you TabItems with Headers set and Content that comes from SomeUserControl which you could design. You don't need to use UserControls, you could just add more UI controls to either DataTemplate. But you will need to add more controls somewhere... and more classes and properties, always remembering to correctly implement the essential INotifyPropertyChanged interface.
And finally, to answer your question in the proper MVVM way... to remove a TabItem, you simply remove the item that relates to that TabItem from the collection. Simple... or it would have been if you really had been using MVVM like you claim. It's really worth learning MVVM properly as you'll soon see the benefits. I'll leave you to find your own tutorials as there are many to chose from.
UPDATE >>>
Your event handling is still not so MVVM... you don't need to pass a reference of any view model anywhere. The MVVM way is to use commands in the view model. In particular, you should investigate the RelayCommand. I have my own version, but these commands enable us to perform actions from data bound Buttons and other UI controls using methods or inline delegates in the view model (where action and canExecute in this example are the CommandParameter values):
<Button Content="Close Tab" Command="{Binding CloseTabCommand}"
CommandParameter="{Binding}" />
...
public ICommand CloseTabCommand
{
get { return new ActionCommand(action => Items.Remove(action),
canExecute => canExecute != null && Items.Contains(canExecute)); }
}
So whatever view model has your Tabs collection should have an AddTabCommand and a CloseTabCommand that add and remove items from the Tabs collection. But just to be clear, for this to work properly, your ClosableTab class should be a data class and not a UI control class. Use a DataTemplate to specify it if it is a UI control.
You can find out about the RelayCommand from this article on MSDN.
I was wandering if someone can explain to me how the Dependency Property DisplayMemberPath works?
I am trying to create Custom ItemsControl that has property like DisplayMemberPath of a ComboBox, in otherwords after setting the ItemsSource I want to be able to specify the Property to Display.
At the moment if I do somthing like:
<cc:MyControl ... DisplayMemberPath="MyObjectDescription" ... >
(Yes I have overridden the DisplayMemberPath, its besides the point).
It displays a list of items, but they each Display "MyObjectDescription", instead of the value that that Property holds for each object in the ItemsSource.
And I believe its because I am missing something in regards to how DisplayMemberPath Property works.
Thanks All. :)
There are two types of DisplayMemberPath. One that supports Binding and one where you have to set a string value. In your case as I can see you wish to implement the second one. To do so create a property inside your custom control of type string and name it DisplayMemberPath. Override the methode OnInitialized in your container with your custom logic where you tell the container to manipulate the path of the binding to DataContext by changing binding's path to the string value as you specified in DisplayMemeberPath. WPF calls OnInitalized once any control is completely initalized but before its about to get rendered. I hope this helps you any futher.
I'm assuming your control is like MyControl and MyControlItem like ListBox and ListBoxItem.
You can access the DisplayMemberPath of MyControl when the MyControlItem is being created and use it to get the data from the DataContext.
Bit late to party, but maybe other could be helped
If your purpose is barely to use Itemscontrol over ListBox/View, you may consider to define the Datatemplate for the itemscontrol's Items instead of packing this in a Usercontrol:
<ItemsControl ItemsSource="{Binding myObjectCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding myObjectProp}"/> (or whatever...)
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
First time attempting MVVM, looking for clarity on where to put some code.
My main view will need to bind to a list that will be holding 1 to many UserControls.
Would the List exist in the ViewModel or the Model? From what I'm reading, the model contains properties typically that the View binds to via the ViewModel. I don't see how that would work for this, the Model would not need to know about the List of UserControls which is a list of a View(UserControl), I may be making this harder than needed, but I'm still wrapping my mind around where code should be placed and I want to understand it. Thanks for any guidance, or if I did not explain myself well please let me know.
Your UserControls should have a ViewModel (Let's call it ItemViewModel by now).
Your MainViewModel should have an ObservableCollection<ItemViewModel>.
Then your view should have an ItemsControl (or one of its derivatives such as ListBox) for which the ItemsSource property will be bound to the ObservableCollection.
And then the ItemsControl should define an ItemTemplate that contains your UserControl.
This is the right way to do what you're describing with WPF / MVVM.
Example (pseudocode):
MainViewModel:
public class MainViewModel
{
public ObservableCollection<ItemViewModel> Items {get;set;}
}
MainView:
<Window>
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<my:UserControl/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
Keep in mind that each instance of UserControl will have it's DataContext set to the corresponding item in the source collection.
Therefore, if your ItemsViewModel looks something like this:
public class ItemsViewModel
{
public string LastName {get;set;}
public string FirstName {get;set;}
//INotifyPropertyChanged, etc.
}
your UserControl can be defined like this:
<UserControl>
<StackPanel>
<TextBox Text="{Binding LastName}"/>
<TextBox Text="{Binding FirstName}"/>
</StackPanel>
</UserControl>
You shouldn't need a list of UserControls. What you would likely have is your View binding to a List of items in your ViewModel. For example, create a ListBox and set it's ItemsSource to your ViewModel's list.
To create a your user control for each item, you would need to create a DataTemplate for the type in your list and specify your UserControl and you can give any bindings inside that usercontrol to the item.
The ListBox will then use the DataTemplate to create a UserControl for each item in the list.
I have a ListView displaying a list of items containing mainly two properties.
Each of these properties should ideally be chosen from two comboboxes.
Moreover, the choices available in the second combobox is depends on the first.
So here is the idea of the code I used:
<ListView>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<ComboBox Name="combo1"
ItemsSource="{DynamicResource combo1Source}"
SelectedItem="{Binding FirstProperty}"
SelectionChanged="combo_SelectionChanged">
<ComboBox Name="combo2"
ItemsSource="{DynamicResource combo2Source}"
SelectedItem="{Binding SecondProperty}">
</StackPanel>
<DataTemplate>
<ListView.ItemTemplate>
</ListView>
The thing is, I don't know how to get the reference to combo2 from within combo_SelectionChanged (in C#).
Could you show me how to proceed?
The easiest thing you can do is add a Tag to combo1:
<ComboBox Name="combo1" Tag="{x:Reference combo2}" ... />
Which you then can just get from the sender in the event handler, e.g.
var combo2 = (sender as FrameworkElement).Tag as ComboBox;
Alternatively you could get the StackPanel from the Parent property and just take (ComboBox)Children[1]. I would not do this though as is breaks if the structure of your template changes.
You should not have a reference to combo2, but you should update the Collection combo2Source which is bound as ItemsSource for combo2...
So in the combo_SelectionChanged you just load the possible values for the actual selection of combo1 to the combo2Source Collection.
EDIT: To prevent thats its for all items the same:
Add a ValueConverter which choses for a selectedItem the corresponding collection of possible values:
<ComboBox ItemsSource="{Binding ElementName=Combo1, Path=SelectedItem, Converter={StaticResource SubSelectionConverter}}" />
Example of ValueConverter:
private Dictionary<Object, List<Object>> _PossibleValues;
public object Convert(Object data, ....)
{
if(PossibleValues.ContainsKey(data))
{
//return the possible values for the actual selected parent item
return(PossibleValues(data));
}
return null;
}
Can have look here on my question and different responses and the solution I found for my specific project:
Find an element in Data Template
Hope this helps.
Regards.