Getting the SelectedItem from a combobox in a DataTemplate - wpf

Lets say I've got a DataTemplate like so
<DataTemplate x:Key="Body">
<StackPanel Orientation="Horizontal">
<ComboBox ItemsSource="{Binding Path=Person.Children}"></ComboBox>
<Button Click="Button_Click">Hello</Button>
</StackPanel>
</DataTemplate>
Which shows a list of ComboBoxes followed by a button.
Now, on clicking the button I need to discover the value in the combo next to the button pressed. I can get the data context as below but can't work out how to get the combos SelectedItem
private void Button_Click(object sender, RoutedEventArgs e)
{
// Can get the data context
var p = ((Button)sender).DataContext as Person;
// How to get the value in the combo ...?
}

you can also reference the combobox in your codebehind if you give it a name. However its cleaner to use a separate class to do your logic than the code behind. Such as a viewmodel.
then you could also do something like this...
<DataTemplate x:Key="Body">
<StackPanel Orientation="Horizontal">
<ComboBox ItemsSource="{Binding Path=Person.Children}"
SelectedItem="{Binding Path=SelectedChild}"
IsSynchronizedWithCurrentItem="True"/>
<Button Command="{Binding Path=ButtonCommand}">Hello</Button>
</StackPanel>
</DataTemplate>

Instead of using the Click event handler, use a Command and bind the CommandParameter property to the ComboBox.SelectedItem. Then in your command's executed logic, you can use the parameter.

Related

Handling Mouse events in Viewmodel in WPF MVVM project

I am using System.Windows.Interactivity.dll and Microsoft.Expression.Interaction.dll to do event handling in Viewmodel in my MVVM WPF project.
below is the code inside my Xaml:
<ItemsControl ItemsSource="{Binding Path= HeaderList}" Grid.Row="0" Grid.Column="0" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}" Width="100" HorizontalAlignment="Left" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseLeftButtonDown">
<ie:CallMethodAction MethodName="PrevMouseDownEventHandler" TargetObject="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
for this I added namespaces in the same Xaml.
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ie="http://schemas.microsoft.com/expression/2010/interactions"
and in my viewmodel I have created a method having PrevMouseDownEventHandler name which is same as that of I mentioned as CallMethod inside EventTigger in the Xaml.
On running my application when I presses mouse button on TextBlock event is generated and look for PrevMouseDownEventHandler method and leave me into following exception:
Could not find method named 'PrevMouseDownEventHandler' on object of type 'string' that matches the expected signature.
this method is as below in my ViewModel.
public void PrevMouseMoveEventHandler(object sender, MouseButtonEventArgs e)
{
// Some implementation here;
}
I don't have any idea where I am going wrong.
Except this all the functionalities inside Viewmodel is working fine for me.
what would be possible solution for this?
CallMethodAction is a delegate with no parameters and no return value. So the "handler" (really an action trigger) would have to look like this:
public void PrevMouseMoveEventHandler()
{
// Some implementation here;
}
Also, you'll need to bind to the View Model (your current binding points to the current item in the ItemsControl). You could do this using RelativeSource binding:
<ie:CallMethodAction MethodName="PrevMouseDownEventHandler"
TargetObject="{Binding Path=DataContext,RelativeSource={RelativeSource AncestorType=ItemsControl}" />
It is looking for the method on the String object you have bound your Text property to.
Basically your data context has changed from the view model to a property of the View Model.

Binding Text Property of child element of ComboBoxItem

I have a WPF ComboBox with a static list of people with an image and first name. I am trying to bind the selected FirstName to a db table. The way I am doing it now (obviously wrong) I am only binding the object and hence writing:
System.Windows.Controls.ComboBoxItem
to my db table.
How can I bind the FirstName from the selected ComboBoxItem from the TextBlock.Text property? Is there a way to do this purely in WPF?
<ComboBox Text="{Binding Path=FirstName}">
<ComboBoxItem>
<StackPanel Orientation="Horizontal" >
<Image Source="/Images/Alice.png" />
<TextBlock Text="Alice" />
</StackPanel>
</ComboBoxItem>
<ComboBoxItem>
<StackPanel Orientation="Horizontal">
<Image Source="/Images/Bob.png" />
<TextBlock Text="Bob" />
</StackPanel>
</ComboBoxItem>
</ComboBox>
I am saving the changes to the db table on a button click event
private void SaveAndClose_Click(object sender, RoutedEventArgs e)
{
bindingView = (BindingListCollectionView)myCollection.View;
bindingView.CommitEdit();
db.SubmitChanges();
}
Instead of hard-coding the comboboxitems, you should use a datatemplate. Then, you should use the SelectedValuePath to select the value based on the FirstName property in your items. Then you can bind to the textbox.
If you want to bind to the textbox in xaml directly, you'll need to name the element (x:Name="myTextBox"). Then, bind your combobox's SelectedValue like so: SelectedValue="{Binding ElementName="myTextBox", Path="Text"}".
In general, I don't think binding a combobox to a textbox is a very good idea.

wpf access lisbox selected item if it isn't string

I have a listbox which displays Name property from an array of Movie objects
<ListBox Name="listBox1" SelectionChanged="listBox1_SelectionChanged">
<ItemsControl ItemsSource="{Binding}" >
<ItemsControl.ItemTemplate >
<DataTemplate >
<TextBlock Name="textBlock1" Text="{Binding Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ListBox>
How can I access the text of the textBlock that's inside the ListBox in Code?
I must use the value of the Name property in my code
The selected item reported by the listbox exposes you the object that owns the Name property bound in the TextBlock. At this point the game is over.
When you do the above every textblock inside the itemscontrol has a name textblock1 that too with a scope limited to each item container.
If you want each of those textblocks individually, I usually do something like:
<TextBlock Text="{Binding Name}" Loaded="TextBlock_Loaded"/>
And in the code register those textboxes in whatever way you wish. A list probably,
List<TextBlock> TextBlockList = new List<TextBlock>();
private void TextBlock_Loaded(object sender, RoutedEventArgs e)
{
TextBlockList.Add((TextBlock)sender);
}
And for example, access the stuff as:
String FirstItem = TextBlockList.ElementAt(0).Text;

Silverlight context menu: how to determine which menu was clicked?

I have the following context menu:
<ListBox x:Name="sectionList" Margin="56,8,15,0" FontSize="64" SelectionChanged="SectionList_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem Header="Hide this section from this list" Click="ContextMenuItem_Click" />
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<TextBlock Text="{Binding DisplayName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
As you can see, each displayed item has its own context menu. Each context menu is hooked up to the same event handler:
private void ContextMenuItem_Click(object sender, RoutedEventArgs e)
{
}
From this method, how can I tell which context menu was clicked? I want to know what the DataContext for the corresponding DataTemplate is.
You can get the item the ListBoxItem is bound to by casting the sender as a FrameworkElement to get access to the DataContext:
(sender as FrameworkElement).DataContext
You can then cast this to the appropriate model class and access the details you need. e.g.:
((sender as FrameworkElement).DataContext as ItemViewModel).DisplayName
If you put a breakpoint inside the event handler ContextMenuItem_Click, you will then be able to examine the properties of sender and e. You will probably find your answer there.
One way to do this is to hover over those words. Another would be to use the Immediate Window. Type in sender and a dot to get intellisense.
If you use <StackPanel Tag="{Binding}"> then ((FrameworkElement)sender).Tag will return the DataContext object (you'll have to cast it before use, of course).

Silverlight: How to dynamically bind a ComboBox in a ListBox ItemTemplate?

I have a list box that requires at least one ComboBox. I couldn't find a way to place the ComboBox in the ItemTemplate I use.
...
<DataTemplate x:Key="parts_template">
<StackPanel Orientation="Horizontal">
<TextBlock .../>
<ComboBox .../>
</StackPanel>
</DataTemplate>
...
<ListBox x:Name="lb_parts" ItemTemplate="{StaticResource parts_template}" .../>
...
How do bind that ComoBox in the DataTemplate to an ObservableCollection in the code behind?
Another thing you could try is subscribe the Loaded event on the ComboBox.
Then you can set the ComboBox.ItemsSource in the EventHandler to MyObservableCollection.
Have a look
XAML:
<DataTemplate x:Key="parts_template">
<StackPanel Orientation="Horizontal">
<TextBlock .../>
<ComboBox Loaded="ComboBox_OnLoaded">
<!-- ComboBox ItemTemplate -->
</ComboBox>
</StackPanel>
</DataTemplate>
C# Code Behind:
private void ComboBox_OnLoaded(object sender, EventArgs e)
{
((ComboBox)sender).ItemsSource = MyObservableCollection;
}
Okay, here is how you can add a ComboBox to the ListBox in the code behind.
Create a ComboBox
ComboBox x = new ComboBox();
If you have a data source that populates the ComboBox then you can just bind that
x.ItemsSource = e.Result;
If you do not and want to manually add items to the ComboBox:
ComboBoxItem y = new ComboBoxItem();
Set the Content of the Item to what you want displayed in the ComboBox
y.Content = "Hello";
Now all that is left, is to add the ComboBoxItem to the ComboBox (only if you are creating the Items manually), and then the ComboBox to the ListBox
x.Items.Add(y);
//testing is my ListBox
testing.Items.Add(x);
You should be able to set the data context to the List itself
lb_Parts.DataContext=myCollection;
Then you should be able to bind to it in the template
<DataTemplate x:Key="parts_template">
<StackPanel Orientation="Horizontal">
<TextBlock .../>
<ComboBox ItemSource={Binding}/>
</StackPanel>
</DataTemplate>

Resources