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.
Related
I am using MVVM approach, my UI needs to provide a simple copy paste area (something like TextBox) to the user and once user enters the text, I need to convert all newline separated text into list items - list items because I want to highlight individual items with errors, if any.
It should be seamless for the user- user should only see List items and should be able to edit them later.
Hence, basically I need a ListBox (for the programmer) which acts like a textbox (for the user). How can I achieve this?
I have implemented this using a Grid with TextBox & ListBox in the same Row, but with some overlap so that user can paste text in textbox and as soon as user enters the text, viewmodel separates the newlines into listitems. It is working but I am facing many issues (like clearing the textbox, etc.). I am looking for better more efficient solution if any. Below is the code snippet from xaml:
<Grid>
<TextBox AcceptsReturn="True" VerticalAlignment="Stretch" BorderBrush="Transparent"
Text="{Binding TextBoxData, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}">
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding OnNewLineCommand}"/>
</TextBox.InputBindings>
</TextBox>
<ListBox ItemsSource="{Binding ListBoxItems}" Margin="0,20,0,0" BorderBrush="Transparent" FocusVisualStyle="{x:Null}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding}" BorderBrush="Transparent"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
</Grid>
Thanks,
RDV
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?
Okay I have a mildly complex piece of functionality here. I'd like to know A) If I am doing it properly. If not, what should I change? 2) If it is proper, what is the best solution to my problem?
I have a main window with a ListView of items. If I click one of these, the right hand Grid Column in this window should populate with a DataGrid with information on the item selected. If I click another item in the ListView, it should change to another DataGrid.
I have seen some ContentPresenter examples but I cannot get this to work, so I stripped it out and will show you the code I have so far. Right now, I only have one item setup, so I will stick with this Driver example.
DriverGrid.xaml
<UserControl DataContext="{Binding AdminDriver, Source={StaticResource Locator}}">
<Grid>
<DataGrid>
//stuff here for datagrid
</DataGrid>
<Button Content="Edit" Command="{Binding ShowEditWindow}" />
<Button Content="Add" Command="{Binding ShowAddWindow}"/>
</Grid>
</UserControl>
AdminDriver.cs (VM)
//Contains variables, and is the datacontext for the above usercontrol
AdminMain.xaml (view for all admin stuff)
//Contains a bunch of junk for the min admin screen which has the listview with the options in it. If you require this piece of code, I can trim it down but I don't se currently see it's relevance. The DataGrid belongs in this window, I'm assuming in a Content Presenter. Here is the ListView that is in column 1 of 2.
<ListView ItemsSource="{Binding AdminMenu}"
Name="AdminFields"
SelectionMode="Single">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand CommandParameter="{Binding SelectedItem, ElementName=AdminFields}" Command="{Binding registerSelected}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<ListView.ItemTemplate>
<ItemContainerTemplate>
<TextBlock Text="{Binding FieldName}"/>
</ItemContainerTemplate>
</ListView.ItemTemplate>
</ListView>
I would take the binding of the DataContext out of the UserControl. This will allow you to use it in other spots if you ever find the need. Instead, just bind the DataContext where you are using it.
<view:YourUserControl DataContext="{Binding AdminDriver}" />
If you are looking to present different UserControls based upon which item is selected in the ListView, then you'll use a ContentPresenter. All the items in your ItemsSource will have the same base class or implement the same interface so that you can put them in the same ObservableCollection. I'll assume AdminDriver would be of that type/interface as well then.
You would set up some DataTemplates at the top of the Window that map the possible real types of the objects in your ItemsSource (AdminMenu) to the UserControl that would represent them.
<Window.Resource>
<DataTemplate DataType="{x:Type model:TypeA}">
<view:UserControlA />
</DataTemplate>
<DataTemplate DataType="{x:Type model:TypeB}">
<view:UserControlB />
</DataTemplate>
//rinse and repeat
</Window.Resource>
Then you'll add a ContentPresenter to the Grid and bind it's DataContext to the AdminDriver property. The UserControl matching the actual type of the selected item as mapped in your DataTemplates will appear.
<ContentPresenter Content="{Binding AdminDriver}" />
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.
Is there a way to fix the known bug of Selection Change Event, selecting does not work if the same item is tapped again.
to give further background, my scenario is that I have four items in my pivot page and when I click one of those items i will be navigated to another page. Now my dilemma is that when i select the same items again, navigation does not work or nothing happens.
Please let me know your suggested fix, thanks much in advance.
<ListBox x:Name="lbviewlist" ItemsSource="{Binding items}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="selectionchanged">
<Command:EventToCommand Command ="{Binding ItemListCommand }" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock TextWrapping="Wrap" Text="{Binding itemName}" FontSize="30" Margin="10,0,0,0" Style="{StaticResource PhoneTextTitle2Style}" Foreground="CadetBlue"/>
<TextBlock TextWrapping="Wrap" Text="{Binding itemDescription}" FontSize="20" Margin="15,5,0,10"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
A good alternative approach is to include all of the content in each item inside a button, which is styled to be invisible so that you never see it, but it covers the surface of the item it encapsulates. If you're using MVVM you can bind the same command property to each of your 'buttons' and bind the DataContext of the button (your data item) to the command parameter. Then whenever you click an item you'll get the command firing every time.
You might want to change your items control to something simple so that the selection changed events dont get in the way.