Possible Mvvm-light bug with EventToCommand - c#

Everything starts pretty simple: i have a ListBoxSelector (app is for wp7). Items consists of picture and some text. Items style is stored inside of the StyleTemplate. Image can be tapped, its event is like
<Style x:Key="ProductSearchListViewerItemStyle" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid>
<Border BorderThickness="0,0,0,0.5">
<Grid>
<Border Grid.Column="0" Background="Transparent">
<Grid> !!! Image is here
</Grid>
<interactivity:Interaction.Triggers>
<interactivity:EventTrigger EventName="Tap">
<command:EventToCommand
Command="{Binding Source={StaticResource ViewModelLocator}, Path=NavigationViewModel.OnProductSearchListViewerTapImageCommand, Mode=OneWay}"
CommandParameter="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext}"/>
</interactivity:EventTrigger>
</interactivity:Interaction.Triggers>
</Border>
...and so on...
Event itself:
private void OnProductSearchListViewerTapImage(ProductItem item)
{
if (item == null) return;
MessengerInstance.Send<ProductItem, ShoppingCartViewModel>(item);
}
So far so good. When running, everything works fine... in 95% cases. I got a list of items with proper items, when i'm pressing on some item's image, event is risen, command is sent, correct item is added to the shopping cart.
BUT. In some cases (problems starts, when tapping 24-25-th item), wrong item is added to the shopping cart (list is rendered properly).
First what i did - checked command: It holds wrong item. Say, i'm tapping on 24-th item, but inside of command, "item" is first from the list.
Second what i did - i grabbed XamlSpy and checked, what is the DataContext of the item. It holds 24-th item, as it should be (because item's image and description is rendered correctly).
That means, the problem is between Event and Command. So, something is wrong inside of EventToCommand.
Anybody have same experience or any ideas?

Actually, i end up moving ControlTemplate to the separate DataTemplate, without using style.
<DataTemplate x:Key="ProductsListViewerItemTemplate">
<Grid>
****
At least, now that bug is not reproducing.

Related

UWP: Highlight ListBoxItem - Events That Coincide with the Highlighting

I have a ListBox bound to a collection of items of type Definition. My requirement is that every time the mouse is hovered over the area of a templated ListBoxItem, a second ListBox open next to the ListBoxItem, revealing sub-items which are of type Word.
(I am basically implementing something similar to a TreeView using two ListBoxes. This is for earlier versions so using a TreeView control is not an option.)
This is the data structure...
public class Word
{
public string Name { get; set; }
}
public class Definition
{
public string Name { get; set }
public ObservableCollection<Word> Words;
}
public class Dictionary
{
public string Name { get; set }
public ObservableCollection<Definition> Definitions;
}
And here is the XAML view...
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Button Grid.Row="0"
Height="0">
<Button.Flyout>
<Flyout x:Name="DefinitionFlyout"/>
<ListBox x:Name="WordsListBox" HorizontalAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemp late x:DataType="local:Word">
<TextBox TextWrapping="NoWrap"
Height="Auto"
BorderThickness="0"
HorizontalAlignment="Stretch"
Text="{x:Bind Name}"
TextAlignment="Left"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Flyout>
</Button.Flyout>
</Button>
<ListBox x:Name="DefinitionsListBox"
Grid.Row="1"
SelectionMode="Single"
HorizontalAlignment="Stretch">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate x:DataType="local:Definition">
<TextBox TextWrapping="NoWrap"
Height="Auto"
BorderThickness="0"
HorizontalAlignment="Stretch"
Text="{x:Bind Name, Mode=TwoWay}">
</TextBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
When the mouse pointer hovers over a Definition item in DefinitionsListBox, WordsListBox should fly out and display the Words of that Definition. And when the pointer exits that Definition and hovers over a new one, I want WordsListBox to reflect that change.
Unfortunately, I can't find the events that will help me accomplish this.
I thought defining PointerEntered and PointerExited in the TextBox of Definition would do the trick but they don't because PointerExited fires IMMEDIATELY after PointerEntered, as in almost simultaneously, and not when the mouse exits the TextBox area. And SelectionChanged of ListBox doesn't fire.
The first event should fire when ListBoxItem highlighting begins, and the second one, when the highlighting ends.
What do you recommend for this, please?
I thought defining PointerEntered and PointerExited in the TextBox of Definition would do the trick but they don't because PointerExited fires IMMEDIATELY after PointerEntered, as in almost simultaneously.
The problem is the when Flyout show at the button there is a mask layer cover on the window. This will prevent basic input event of TextBox defined. It looks PointerExited fires immediately after PointerEntered as in almost simultaneously.
For solving this , you could set OverlayInputPassThroughElement property for Flyout that make the area of ListBox could response PointerEntered PointerExited event when Flyout is opened. For more please refer the following code.
<Flyout x:Name="DefinitionFlyout" OverlayInputPassThroughElement="{x:Bind DefinitionsListBox}">

Settings Window design - binding Page to TreeViewItem

I'm just starting with WPF (coming from WinForms) and I want to make settings window like the one in Visual Studio. Category tree on left side and appropriate views on the right.
In WinForms I did it by adding to TreeViewItem's TAG name of the window that should be displayed and then in OnClick I was creating that window using reflection. Something like this:
//pseudocode
TreeViewItem item = CreateTreeViewItem();
item.Tag = "GeneralSettingsWindow";
item.Text = "General settings";
------------------------------------------------------------------------
void ItemClick(object sender)
{
TreeViewItem item = sender as TreeViewItem;
string formName = item.Tag.ToString();
BaseSettingsForm f = Activator.CreateInstance(formName);
settingsPanel.Controls.Clear();
settingsPanel.Controls.Add(f);
}
And it worked fine.
I'm curious how can I achieve that functionality using WPF binding. Is it possible anyway?
I have done a tree view in left side and when you click an item it will update the contents of right side panel. In order to do that you have to make two panels or grid in an window, one in left side with less width and another in right side. Then put the tree view in the left side and update the contents of right side grid by adding different types of user controls or by showing and hiding different grids with the corresponding click of tree view item.
Here is an example of making the tree view and you can modify that according to your own requirement.
<TreeView MouseLeftButtonUp="Get_MFR" SelectedItemChanged="treeviewQuestion_SelectedItemChanged"
x:Name="treeviewQuestion" FontFamily="Nunito" FontSize="16" Padding="10 10"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
BorderThickness="0" Margin="0,0,0,27"
>
<TreeView.Resources>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True"/>
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="10 2">
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}},Path=Tag,
Converter={x:Static local:TagToTextConverter.Instance}}" />
<TextBlock Text="{Binding}"/>
<Rectangle Margin="5" Width="10" Height="10" Fill="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type TreeViewItem}},Path=Tag,
Converter={x:Static local:TagToColorConverter.Instance}}"/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TreeView.Resources>
</TreeView>
Even though you're new to WPF development, I'd still suggest you take a look at the MVVM design pattern. The separation between user interface and business logic makes this type of application structure much more straightforward.
Replace your settings panel with a ContentControl, and define as many DataTemplates as you require for different tree node types. You can then bind its Content property to the selected TreeViewItem.
Unfortunately, "out of the box" WPF doesn't support binding to the selected item in a TreeView. However, there are several ways around that, including using a Behaviour, which I described in a recent blog post.

Xaml Listbox item focus issue

I am using a xaml UserControl as part of a WPF application. i have created a list box which i have populated with data from a text search. this data appears on buttons which are used to select the users desired option from the search.
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
Margin="2">
<ListBox ItemsSource="{Binding Path=CrewOptions}"
HorizontalContentAlignment="Stretch"
BorderThickness="0"
SelectionMode="Multiple"
Name="CrewOptionsListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<Button x:Name="irrelevant"
Height="28px"
Background="#F4F3E9"
Margin="2,2,2,2"
Content="{Binding irrelevant1, TargetNullValue={x:Static sys:String.Empty}}"
Command="{Binding irrelevant2}"/>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}"
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</ScrollViewer>
This works fine, however when i tab from the textbox into the list box and then use the arrow keys to select an option, the enter key press does not select the button. Instead, i have to tab once more to focus on the button before pressing enter to select it.
Is there any way to avoid having to hit the last tab key to focus on the button?
Open to both Xaml and C# solutions (preferably MVVM)
Hi you can add below code in ListBox this will resolve focus issue on ListBoxItem.
<ListView.ItemContainerStyle>
<Style TargetType="ContentControl">
<Setter Property="Focusable" Value="False"/>
</Style>
</ListView.ItemContainerStyle>
The reason for this is that you have cascading controls. When you use the arrow keys the List box is the active control, therefore all events will be fired based on the list box, not the button.
One way forward is to assign a keypressed event on the selected item, then initiate the function that will be triggered by the button.
something like:
listBox_keyPressed()
{
if(selecteditem)
{
DoSomethingFor(selectedItem);
}
}

VisualBrush (linked to VLC) doesn't change on TabControl selection change

I am having a weird experience with a combination of visual brush (linked to a VLC player through VLC.DotNet) and a tab control. I have created a custom control using the VLC player to watch an RTSP stream and have multiple of these controls in a given window.
The problem is that if I put all the controls in a list view they all display properly. But if I put them in a tab control then it always shows the stream of the first-selected tab item, not matter what tab I'm currently on. Everything else in the control (label, etc.) changes properly, but not the part drawn by the visual brush.
The view for my control is defined as:
<UserControl x:Class="myApp.View.CameraMonitorView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Wpf="clr-namespace:Vlc.DotNet.Wpf;assembly=Vlc.DotNet.Wpf"
Loaded="UserControl_Loaded">
<DockPanel>
<Label Content="{Binding Name}" DockPanel.Dock="Bottom"/>
<Grid Margin="3">
<Grid.Background>
<VisualBrush Stretch="Uniform">
<VisualBrush.Visual>
<Image Source="{Binding VideoSource, ElementName=vlcControl}"/>
<!--<Image Source="{Binding Image}" /> -->
</VisualBrush.Visual>
</VisualBrush>
</Grid.Background>
<Wpf:VlcControl x:Name="vlcControl" Margin="3"/>
</Grid>
</DockPanel>
</UserControl>
The code behind for the view starts playing the RTSP, but I don't think that code will help with this problem.
Meanwhile the ViewModel (stripped down for ease of viewing) is just:
class CameraMonitorViewModel : ViewModelBase
{
public CameraMonitorViewModel(string name, string image)
{
Name = name;
Image = image;
}
public string Name {get; set;}
public string Image { get; set; }
}
And I have a data template defined as:
<DataTemplate DataType="{x:Type vm:CameraMonitorViewModel}">
<v:CameraMonitorView HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</DataTemplate>
The full window view model has an ObservableCollection called Monitors and the view displays:
<TabControl ItemsSource="{Binding Monitors}" SelectedIndex="0" Height="300">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Header" Value="{Binding Name}"/>
<Setter Property="Content" Value="{Binding}"/>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
<ListView ItemsSource="{Binding Monitors}" Height="300" />
The ListView properly shows different images from each camera. The tab item will always show the same camera, but the control's label item changes when I clicked different tabs. Moreover, if I replace the databinding to the VLC control's image to the commented out static image object (whose source is set by way of the view model) then the image will properly change when I click different tabs.
I'm really confused and would appreciate any help that could be provided.
Yeah, as I noted in the comment to the question the problem was that I was starting the stream playing based on the Loaded event for the control. But it seems like it doesn't get fired after the second tab, probably because of what Rachel mentions here: Loaded event doesn't fire for the 4th TabControl tab (being that it reuses the template rather than loading multiple ones).

WPF Custom "Toolbox" Selector control

I am trying to create a custom control, which behaves a bit like one of the "rows" in the toolbox in Expression Blend.
When closed, it displays the first of its items, and when the user holds the mouse down over it for a second or so, it "expands" to reveal the other items in a popup excluding the item which is currently selected (the item which was clicked on still remains visible, but is not grouped with the others).
I have managed to make a control which inherits from ContentControl displays one item and then reveals the others when expanded, but the item which is displayed first does not change when one is clicked.
The template is as follows:
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<ContentPresenter Content="{TemplateBinding MainItem}" /> // The item that is seen even when not expanded
<Popup Name="Popup" Placement="Right" IsOpen="{TemplateBinding IsExpanded}" AllowsTransparency="True" Focusable="False" PopupAnimation="Fade">
<Border Name="SubmenuBorder" SnapsToDevicePixels="True" BorderThickness="0" >
<StackPanel Orientation="Horizontal" IsItemsHost="True" /> // The items only seen when expanded
</Border>
</Popup>
</Grid>
At the moment, I have to manually set the MainItem in XAML and it is not a member of the Items property of the ContentControl.
Is there a way to achieve this functionality where all items are part of the Items collection and are automatically not shown when an IsSelected property or something is set, and are then shown where the current MainItem is shown when this is the case?
I tried changing it to a Selector and using a filter on the Items to achieve this and binding the first ContentPresenter to the SelectedItem but it didn't seem to work.
If possible, the order of the items only visible when the control is expanded should be the same as the order in which they are laid out in XAML, with the currently selected item missing.
Thanks
try using the ItemContainerStyle to set the visibility of the selected item to 'Hidden'. You'd need a BoolToVisibilityConverter (you can write one easily or get one from the WPFToolkit) to get the correct value
<ContentPresenter Content="{Binding ElementName=selector, Path=SelectedItem}" />
<ComboBox x:Name="selector">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="Visibility" Value="{Binding Path=IsSelected, Converter={StaticResource boolToVisibilityConverter}}" />
</Style>
</ComboBox.ItemContainerStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding SomeProperty}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>ComboBoxItem
I managed to solve the problem by using a VisualBrush to create a preview of the item without having to move it within the visual tree.

Categories