WPF Mouse Binding on Listbox Item Template Inconsistent - wpf

I have a listbox which binds to an observable collection of a custom type which displays each item through my data template:
<ListBox x:Name="ListBox" Style="{StaticResource CustomListBox}" ItemsSource="{Binding HandStats}" Height="410" Width="150" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding HoleCards[0].CardParts.Value}" Margin="2"/>
<Image Source="{Binding HoleCards[0].SuitImagePath}" Width="10" Height="10" Margin="2"/>
<TextBlock Text="{Binding HoleCards[1].CardParts.Value}" Margin="2"/>
<Image Source="{Binding HoleCards[1].SuitImagePath}" Width="10" Height="10" Margin="2"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Runs:" Margin="2"/>
<TextBlock Text="{Binding NumberOfRuns}" Margin="2"/>
<TextBlock Text="Players:" Margin="2"/>
<TextBlock Text="{Binding NumberOfPlayers}" Margin="2"/>
</StackPanel>
<StackPanel.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding Path=DataContext.PopulateReport, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
CommandParameter="{Binding ElementName=ListBox, Path=SelectedItem}"/>
</StackPanel.InputBindings>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Image of my listbox:
http://imgur.com/a/QGz8z
Now ideally when I click anywhere in the stack panel for each item, the item will be selected and the command will be fired. But my problem is that the item selection and the command firing is not working as it should.
The item will only become selected if I click in between the text blocks or in the empty space to the right of the text blocks, but the command will not be fired. The command will only fire once I have firstly selected the item, and then click again onto one of the text blocks or one of the images.
I'm still quite new to WPF so if I'm not making much sense please let me know, thanks in advance :)

So instead of using input bindings I decided to use a different method. I created a property in my vm and bound this to the selected item of my list. So now every time I select a new item (which works perfectly now) the property is updated in my vm. Simple!

Try to set the Background property of the StackPanel to Transparent:
<StackPanel Orientation="Vertical" Background="Transparent">
...
If it doesn't have a Background the click events won't be raised as expected.

Related

Wpf Expander Collection, how to have parent ViewModel know which Expander object current?

In a view which contains an item having a collection of child items, I have an ItemsControl which hosts the child items collection. The individual items are contained in an Expander. When a child item is expanded, I need the parent view model to be aware of which child is being acted upon. I was able to implement an Event Trigger which passes the child object as a parameter to the parent view model, and the parent view model can then set a SelectedChildObject property. That is what I need but where it falls short is when multiple items are expanded, and the user acts on an item which is not in the most recently expanded item. When this happens the item they are interacting with does not match the SelectedChildObject property, since only the most recently expanded object would be the property value.
I have seen the solutions which use a ListBox to contain the Expander, and then set the Expander IsExpanded based on the ListBox IsSelected, but I don't like this solution because it only allows one expander open at a time, plus it does not seem possible to have the Expander stretch to fill all of the space in the ListBox, and this look does not look good.
Is there a way I can always let the parent view model know which child object is being acted upon?
<ItemsControl Grid.Row="0" Grid.Column="0"
ItemsSource="{Binding Slots, Mode=TwoWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Padding="10">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Expanded">
<i:InvokeCommandAction Command="{Binding DataContext.ExpandedCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Expander.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="32" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Row="0" Grid.Column="0"
Height="16" Width="16" Source="/WpfApp1;component/Assets/Images/Warning.ico" />
<StackPanel Grid.Row="0" Grid.Column="1"
Orientation="Horizontal">
<Label Content="Slot: " />
<Label Content="{Binding SlotNumber, Mode=TwoWay, ValidatesOnNotifyDataErrors=False}" />
</StackPanel>
</Grid>
</Expander.Header>
<StackPanel Margin="20">
<StackPanel Orientation="Horizontal">
<Label Content="Slot Number:" Margin="0 5 2 0" Width="100" />
<TextBox Text="{Binding SlotNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, ValidatesOnExceptions=True, ValidatesOnNotifyDataErrors=True, NotifyOnValidationError=True}"
Style="{DynamicResource Configuration.Input.TextBox}"
Width="30" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="Modules:" Margin="0 5 2 0" Width="100" />
<ListBox ItemsSource="{Binding Source={x:Static local:SlotsViewModel.AllowedModules}}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}"
Command="{Binding DataContext.AddRemoveAllowedModuleCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
CommandParameter="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</StackPanel>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
What kind of actions are you trying to perform on Slots?
Something like removing the last manipulated slot? If so then you will probably have to write a behavior or a control that will capture PreviewMouseDown events and send a command/event to notify your parent View Model. But it's not ideal.
And any way you will probably want the "Selected slot" to be highlighted befor it can be removed. You could also try to remove background of the expander and use the ListBox. So that when you click on the background of your Slot it selects and highlights the ListBoxItem. In this way you can bind SelectedItem to the VM.

Commanding with Static Items of Listbox using MVVM

I have a list box in my view with three static items(Name,Age,Gender), and I want my ViewModel to do something when an item in the ListBox is selected. I want to do this without any code-behind, using the MVVM pattern.
My goal is to navigate to a page when an item is selected also the items stated above does not come from an observable list, it is hardcoded in my XAML. How would I do that? Please teach me. If you don't mind to send a sample, that would be a great help. Thanks much in advance.
<ListBox x:Name="lbviewlist">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged" >
<Command:EventToCommand Command ="{Binding ViewCommand}"
PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.Items>
<StackPanel x:Name="lbiview1" Orientation="Vertical">
<ListBoxItem Content="Name" FontSize="35" Margin="10,0,0,0"
Foreground="OrangeRed"/>
<TextBlock TextWrapping="Wrap" Text="View name of the patient"
FontSize="25" Margin="10,0,0,0" Foreground="White"/>
</StackPanel>
<StackPanel x:Name="lbiview2" Orientation="Vertical">
<ListBoxItem Content="Age" FontSize="35" Margin="10,20,0,0"
Foreground="OrangeRed"/>
<TextBlock TextWrapping="Wrap" Text="View age of the patient"
FontSize="25" Margin="10,0,0,0" Foreground="White"/>
</StackPanel>
<StackPanel x:Name="lbiview3" Orientation="Vertical">
<ListBoxItem Content="Gender" FontSize="35" Margin="10,20,0,0"
Foreground="OrangeRed"/>
<TextBlock TextWrapping="Wrap" Text="View the gender of the patient"
FontSize="25" Margin="10,0,0,0" Foreground="White"/>
</StackPanel>
</ListBox.Items>
</ListBox>
You can use data binding to bind the SelectedItem of the ListBox to a property on your view model. Then, in the setter of your view model property, you can run the logic you need to when the selected item changes.
<ListBox SelectedItem="{Binding MySelectedItem}" ... />
Update
Ok, firstly I would strongly recommend (no insist) that you use an MVVM framework. If you're doing MVVM, then you need to use a framework.
Next, there's no reason why these list items can't be on your view model. It would certainly make your view a lot cleaner.
Then you can set your ListBox ItemsSource to bind to your view model collection, and then use data templating in your view to render each item in the list consistently.
E.g. your view can end up something like:
<ListBox ItemsSource="{Binding MyItems}" SelectedItem="{Binding MySelectedItem}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Value}" ... />
<TextBlock Text="{Binding Description}" ... />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Much cleaner, I think you'll agree.

How do I Raise an Event when the Binding is completed?

I have a TextBox in my application that i am developing and the binding is done in the xaml
I want to be able to raise an event when the binding of the TextBox Text Property is completed meaning when the text is loaded/changed through binding into the TextBox how do i achive this?
I tried TextChanged Event also Loaded Event but no luck right after the Binding is complected so how do i do it?
<ListBox x:Name="listBoxCategories" Background="Transparent" BorderThickness="0" Grid.Row="4" Grid.ColumnSpan="2" Margin="0" Padding="0" ItemContainerStyle="{StaticResource ListBoxItemStyle}" ItemsSource="{Binding ElementName=discussion_categoryDomainDataSource, Path=Data}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="stackPanelCategory" Orientation="Vertical" Margin="0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<TextBlock x:Name="TextBlockCategoryId" Text="{Binding Path=CategoryID}" />
<toolkit:Expander IsExpanded="True" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Style="{StaticResource ForumExpanderStyleRight}">
<toolkit:Expander.Header>
<TextBlock FontSize="16" FontWeight="Bold" Foreground="White" Margin="4,0,4,0" Text="{Binding CategoryName}" LayoutUpdated="TextBlock_LayoutUpdated" />
</toolkit:Expander.Header>
<ListBox x:Name="listBoxBoards" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="Transparent" BorderThickness="0" Margin="0" Padding="0" ItemContainerStyle="{StaticResource ListBoxItemStyle}" ItemsSource="{Binding CategoryBoards}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0" HorizontalAlignment="Stretch" x:Name="GridBoard">
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</toolkit:Expander>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
at this line
<TextBlock x:Name="TextBlockCategoryId" Text="{Binding Path=CategoryID}" />
I want an event where when the Binding is completed i want to give a different data source for the list box under the parent stack panel so i need the binding event so when i have the catedory id int he text block which later will be hidden in the xaml ( which now is not collapsed for testing reasons )
This is a slightly peculiar thing to want to do, it might help if you tell us why you are wanting to do this?
However, with no other information to hand, I will propose a solution.
If the TextBox API events do not provide the infomation you need, try handing the LayoutUpdated event. This event fires each time layout occurs, which is typically as elements ae added to the visual tree. WHen this event fires inspect your TextBox.Text property to see if it is set.
This solved the problem for me :)
<TextBlock FontSize="16" FontWeight="Bold" Foreground="White" Margin="4,0,4,0" Text="{Binding CategoryName}" Loaded="TextBlock_Loaded" />
Loaded event in the inner Text block rather than the upper textblock because by the time this is loaded the upper text block's text is already bound so i can retrive the ID :)

Listbox on one page calling SelectionChanged event handler of ListBox on AnotherPage?

I have a two pages say Main.xaml and Details.xaml.Each page has a ListBox and I am setting each of it to a collection in ViewModel(Same collection).The strange thing is that when i select an item in Details page it calls the SelectionChanged event handler on Main page and Details page.Is this a bug?
I have solved the problem by unhooking SelectionChanged eventhandler in OnNavigatedFrom() method.
EDIT
In Main.Xaml I have something like below:
<ListBox Name="MainDataListBox" Margin="8,113,8,8" ItemsSource="{Binding DataList}" SelectionChanged="MainDataListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<Border BorderThickness="3" BorderBrush="#A5FFFFFF" Width="80" Margin="0,20,0,20" Height="60">
<Image Source="{Binding ImageUrl, Mode=OneWay}" VerticalAlignment="Stretch" Margin="0,0,0,0" Width="80" Height="60" Stretch="Fill" />
</Border>
<TextBlock TextWrapping="Wrap" Text="{Binding Title}" FontSize="40" FontWeight="Normal" VerticalAlignment="Center" Margin="30,0,0,0" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In Details.xaml also I have same kind of ListBox:
<ListBox Name="DetailDataListBox" Margin="8,113,8,8" ItemsSource="{Binding DataList}" SelectionChanged="DetailDataListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<Border BorderThickness="3" BorderBrush="#A5FFFFFF" Width="80" Margin="0,20,0,20" Height="60">
<Image Source="{Binding ImageUrl, Mode=OneWay}" VerticalAlignment="Stretch" Margin="0,0,0,0" Width="80" Height="60" Stretch="Fill" />
</Border>
<TextBlock TextWrapping="Wrap" Text="{Binding Title}" FontSize="40" FontWeight="Normal" VerticalAlignment="Center" Margin="30,0,0,0" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Both of the Pages Main and Details have seperate EvenHandlers MainDataListBox_SelectionChanged and DetailDataListBox_SelectionChanged.
The problem is when i select an Item in Details Page MainDataListBox_SelectionChanged is called first and then DetailDataListBox_SelectionChanged.
Also my viewmodel is a static one in App.cs and I am setting the data context of both these pages to same viewmodel.
Thanks and Regards
vaysage
As both listsboxes were bound to the same dataset a change in the selected time in one list would have affected the other.
Obviosuly you're found a work around but I'd recommend having a unique view model for each page. If the page is just a different way of displaying the same data use a single page and alter the display as appropriate. (Creating your own visual states may be a good way to do this.)

Silverlight 3 Datatemplate: Firing a button_click event

I have a datatemplate for a listbox stored in the ResourceDictionary. The template contains a button which when clicked should pass the listbox item to a seperate listbox
<DataTemplate x:Key="ListBoxContentPresenterTemplate">
<StackPanel Orientation="Horizontal">
<StackPanel Height="75" Width="100">
<TextBlock x:Name="Surname" Text="{Binding Property1}" FontFamily="Arial" FontSize="16" FontWeight="Bold" d:LayoutOverrides="Width"/>
<TextBlock x:Name="Firstname" Text="{Binding Property2}" Foreground="#FFC9C23E" FontFamily="Arial" FontSize="12" d:LayoutOverrides="Width"/>
</StackPanel>
<Button x:Name="Button1" Content="Press" />
</StackPanel>
</DataTemplate>
I don't know how to fire the event as adding it in the xaml as I normally would doesn't work here(presumably as it's a template).
Any help would be much appreicated.
DataTemplate is not the problem to subscribe to events. May be only if your DataTemplate is located in a separate resources file, so you have no rational place to place the event handler. If so you can make use of Commands (I believe this will work in version 3 too).

Resources