Commands causing slow down - wpf

First off, I'm fully aware of how contrived this example is, and it's lack of virtualization, but I'm using it to demonstrate a problem I've been experiencing in a more complicated system that would be very difficult to show. And this seems like a simple way to demonstrate it.
The following code creates a menu and an ItemsControl bound to a large list of strings(I'm using 10,000), each being viewed as a button.
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="Test">
<MenuItem Header="A" />
<MenuItem Header="B" />
<MenuItem Header="C" />
</MenuItem>
</Menu>
<Expander IsExpanded="True">
<ScrollViewer>
<ItemsControl ItemsSource="{Binding LotsOfStrings}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="Open" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Expander>
</DockPanel>
As expected, this is slow to load, but once it has loaded, the 'Test' menu is responsive (that is, there is no noticeable lag as you mouse over the menu items).
Now make the following small change of adding a command to the button.
<Button Content="Open" Command="Open"/>
Now the Menu becomes horribly laggy. For some reason, mousing over different menu items calls CommandManager.InvalidateRequerySuggested() which gets executed for each button, causing the slow down.
So I'm wondering if there is anyway of disabling the Commanding updates on controls that aren't visible? so in this example, if the expander is collapsed, can I some how stop the buttons from still handling the commanding updates?

Related

Xamarin.Forms for WPF TapGestureRecognizer double-click not firing for a ListView

I have a simple app that I want to do in Xamarin, and I have two pages with ListViews with a simple label for each cell.
I want to be able to both SELECT a line (click), and initiate an EDIT of the line by double-clicking. I was able to do this in UWP with TapGestureRecognizer with NumberOfTapsRequired="2".
But, this doesn't seem to work with UWP. I thought maybe I had to switch to ClickGestureRecognizer, but I could not get that to work at ALL in WPF. In desperation, I'm provide the user a "context action" for edit, which (gladly), does work, but is not preferred.
<ListView x:Name="itemList" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.ContextActions>
<MenuItem
Clicked="MenuItemEdit_Clicked"
CommandParameter="{Binding .}"
Text="Edit" />
</ViewCell.ContextActions>
<Label FontSize="Small" Text="{Binding}" >
<Label.GestureRecognizers>
<TapGestureRecognizer
NumberOfTapsRequired="2"
Tapped="TapGestureRecognizer_2Tapped" />
</Label.GestureRecognizers>
</Label>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Xamarin.Forms/
Has anyone else tried/seen this? Any other suggestions? And, any thoughts on how much WPF will be supported going forward?

ListView with VirtualizedWrapPanel without scrolling

I have fixed a couple of lines in this implementation of a VirtualizedWrapPanel.
Ok, the window in which I have put the ListView with the VirtualizedWrapPanel as the ListView's ItemsPanel shoud not be scrollable. Instead of scrolling, the user will initiate something like pages change by clicking the button. So I'll should somehow bring into view "the next portion of items" as a response to the button click.
Here is the ListView which I have described:
<ListView x:Name="StationsListView"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
BorderThickness="0"
DataContext="{StaticResource ViewModelKey}"
SelectionMode="Extended"
Grid.Row="1" ItemsSource="{Binding Stations}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<common:VirtualizingWrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Button Style="{DynamicResource DestinationButtonStyle}">
<TextBlock Text="{Binding FullName}"
Style="{DynamicResource DestinationStationTextBlockStyle}"
TextTrimming="CharacterEllipsis" />
</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
So, how can I scroll to the next portion of items manually?
After looking through the code example from your link, I'm not convinced of the author's knowledge on the subject of virtualization. The code seems to be more complicated and less efficient than it needs to be.
I have a WPF book that explains virtualisation very well with examples and you're in luck, because someone has published it online. I'm not sure if it is legal, so I can't verify how long this link will work, but it does now: Take a look at chapter 8 in Control Development Unleashed online.

Silverlight 4 tab order problem?

I’d like to find out if I am missing something in my XAML code related to the tabbing. In my case, I need to click twice to be able to advance to the next control in the tab order. It will be great to find out the proper way applying tabbing in Silverlight 4. Below is the code I use for tabbing. Thank you in advance!
<StackPanel x:Name="sp" Grid.Column="0" >
<TextBlock x:Name="txtO" Style="{StaticResource AVV_TitleStyleBlue}" Text="Text" />
<HyperlinkButton x:Name="hl1" Style="{StaticResource AVV_HyperlinkButtonStyle}" Content="test 1" IsTabStop="True" TabIndex="11" />
<HyperlinkButton x:Name="hl2" Style="{StaticResource AVV_HyperlinkButtonStyle}" Content="test 2" IsTabStop="True" TabIndex="12"/>
<HyperlinkButton x:Name="hl3" Style="{StaticResource AVV_HyperlinkButtonStyle}" Content="test 3" IsTabStop="True" TabIndex="13" />
<HyperlinkButton x:Name="hl4" Style="{StaticResource AVV_HyperlinkButtonStyle}" Content="test 4" IsTabStop="True" TabIndex="14" />
</StackPanel>
You might have the issue where a sub control is also getting the tab, i guess that since you need to tab twice. On the first tab is most likely taking you to a hidden control, The second tab then takes you to the control you want. To fix this you need to find out what control the focus is going to when you hit Tab. You can do that by using the Focus Manager's get Focused Component method; you put that in a place just after you hit tab, you may need a small delay before getting the control. Then you set a break point in VS, and get the info about that control. Once found you set its IsTabStop Property to false. If you are using a third party control, you will need to change its template maybe using expression blend.

Button in a WPF ListItem

I'm a bit new to WPF. I am working on a list UI where each item in the list will have a set of corresponding buttons to operate on that particular data item.
Coming from a web background, I normally would have bound the value into a hidden element in that particular list item or something. However, I just need to find the corresponding technique in this WPF world :-)
The most common technique is to use templates. Please consider using my example of a templated ListItem (for example ListBoxItem):
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Command="{Binding Path=YourCommand}" Content="Dynamic Button 1" />
<Button Command="{Binding Path=YourSecondCommand}" Content="Dynamic Button 2" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Please feel free to ask if you have any questions/ideas.

How to set CommandTarget for MenuItem inside a ContextMenu?

(This question is related to another one, but different enough that I think it warrants placement here.)
Here's a (heavily snipped) Window:
<Window x:Class="Gmd.TimeTracker2.TimeTrackerMainForm"
xmlns:local="clr-namespace:Gmd.TimeTracker2"
xmlns:localcommands="clr-namespace:Gmd.TimeTracker2.Commands"
x:Name="This"
DataContext="{Binding ElementName=This}">
<Window.CommandBindings>
<CommandBinding Command="localcommands:TaskCommands.ViewTaskProperties"
Executed="HandleViewTaskProperties"
CanExecute="CanViewTaskPropertiesExecute" />
</Window.CommandBindings>
<DockPanel>
<!-- snip stuff -->
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- snip more stuff -->
<Button Content="_Create a new task" Grid.Row="1" x:Name="btnAddTask" Click="HandleNewTaskClick" />
</Grid>
</DockPanel>
</Window>
and here's a (heavily snipped) UserControl:
<UserControl x:Class="Gmd.TimeTracker2.TaskStopwatchControl"
xmlns:local="clr-namespace:Gmd.TimeTracker2"
xmlns:localcommands="clr-namespace:Gmd.TimeTracker2.Commands"
x:Name="This"
DataContext="{Binding ElementName=This}">
<UserControl.ContextMenu>
<ContextMenu>
<MenuItem x:Name="mnuProperties" Header="_Properties" Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}"
CommandTarget="What goes here?" />
</ContextMenu>
</UserControl.ContextMenu>
<StackPanel>
<TextBlock MaxWidth="100" Text="{Binding Task.TaskName, Mode=TwoWay}" TextWrapping="WrapWithOverflow" TextAlignment="Center" />
<TextBlock Text="{Binding Path=ElapsedTime}" TextAlignment="Center" />
<Button Content="{Binding Path=IsRunning, Converter={StaticResource boolToString}, ConverterParameter='Stop Start'}" Click="HandleStartStopClicked" />
</StackPanel>
</UserControl>
Through various techniques, a UserControl can be dynamically added to the Window. Perhaps via the Button in the window. Perhaps, more problematically, from a persistent backing store when the application is started.
As can be seen from the xaml, I've decided that it makes sense for me to try to use Commands as a way to handle various operations that the user can perform with Tasks. I'm doing this with the eventual goal of factoring all command logic into a more formally-defined Controller layer, but I'm trying to refactor one step at a time.
The problem that I'm encountering is related to the interaction between the command in the UserControl's ContextMenu and the command's CanExecute, defined in the Window. When the application first starts and the saved Tasks are restored into TaskStopwatches on the Window, no actual UI elements are selected. If I then immediately r-click a UserControl in the Window in an attempt to execute the ViewTaskProperties command, the CanExecute handler never runs and the menu item remains disabled. If I then click some UI element (e.g., the button) just to give something focus, the CanExecute handlers are run with the CanExecuteRoutedEventArgs's Source property set to the UI element that has the focus.
In some respect, this behavior seems to be known-- I've learned that menus will route the event through the element that last had focus to avoid always sending the event from the menu item. What I think I would like, though, is for the source of the event to be the control itself, or the Task that the control is wrapping itself around (but Task isn't an Element, so I don't think it can be a source).
I thought that maybe I was missing the CommandTarget property on the MenuItem in the UserControl, and my first thought was that I wanted the command to come from the UserControl, so naturally I first tried:
<MenuItem x:Name="mnuProperties"
Header="_Properties"
Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}"
CommandTarget="{Binding ElementName=This}" />
This failed as an invalid binding. I'm not sure why. Then I thought, "Hmmm, I'm looking up the tree, so maybe what I need is a RelativeSource" and I tried this:
<MenuItem x:Name="mnuProperties"
Header="_Properties"
Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}"
CommandTarget="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:TaskStopwatchControl}}}" />
That also failed, but when I looked at my xaml again, I realized that the ContextMenu is in a property of the UserControl, it's not a child element. So I guessed (and at this point it was a guess):
<MenuItem x:Name="mnuProperties"
Header="_Properties"
Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}"
CommandTarget="{Binding RelativeSource={x:Static RelativeSource.Self}}" />
And that also failed.
One failed guess-and-check like this is enough to make me back off and realize that I'm missing some sort of fundamental concept here, though. So what do I do?
Is my understanding re: the role of CommandTarget correct in that this provides a mechanism to modify the source of a command?
How do I bind from a MenuItem in UserControl.ContextMenu to the owning UserControl? Or am I doing something wrong simply because I perceive a need to?
Is my desire to have the context of a command set by the element that was clicked to generate the context menu, as opposed to the element that had focus before the context menu, incorrect? Perhaps I need to write my own command instead of using the RoutedUICommand:
private static RoutedUICommand viewTaskPropertiesCommand = new RoutedUICommand("View a task's details.", "ViewTaskProperties", typeof(TaskCommands));
public static RoutedUICommand ViewTaskProperties
{
get { return viewTaskPropertiesCommand; }
}
Is there some deeper fundamental flaw in my design? This is my first significant WPF project, and I'm doing it on my own time as a learning experience, so I'm definitely not opposed to learning a superior solution architecture.
1: Yes, CommandTarget controls where the RoutedCommand starts routing from.
2: ContextMenu has a PlacementTarget property that will allow access to your UserControl:
<MenuItem x:Name="mnuProperties" Header="_Properties"
Command="{x:Static localcommands:TaskCommands.ViewTaskProperties}"
CommandTarget="{Binding PlacementTarget,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContextMenu}}}"/>
To avoid repeating this in every MenuItem you could use a Style.
3 & 4: I would say your desire is reasonable. Since the Execute handler is on the Window it doesn't matter right now, but if you had different regions of the application, each with their own Execute handler for the same command, it would matter where the focus was.
Similar solution I found was using the Tag property of the parent to grab the datacontext:
<Grid Tag="{Binding Path=DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
<Grid.ContextMenu>
<ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem
Header="{Binding Path=ToolbarDelete, Mode=Default, Source={StaticResource Resx}}"
Command="{Binding RemoveCommand}"
CommandParameter="{Binding DataContext.Id, RelativeSource={RelativeSource TemplatedParent}}"/>
</ContextMenu>
</Grid.ContextMenu>
<TextBlock Text="{Binding Name}" Padding="2" />
</Grid>

Resources