WPF SelectedItem working inconsistently....(MVVM data binding) - wpf

I have a WPF ListBox that is a child of a ListView.
The ListBox has a 'SelectedItem' attribute that is bound to a property on my ViewModel SelectedTask.
When I click a row on my ListBox, then it updates SelectedTask. But only sometimes.....
By sometimes:
Every List Box row will update SelectedTask at least once
The ones in the "middle" of the screen will always update SelectedTask. I can hop from one row to another and back again, each time SelectedTask is updated
The ones at the top or bottom of the screen will update SelectedTask, but maybe only once. If I select another row and then return to this one, I can not get SelectedTask to be updated.
A highly cut-down/edited view of my XAML is as follows:
<ListView ItemsSource="{Binding WorkItems}" >
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/>
<ColumnDefinition Width="850"/>
</Grid.ColumnDefinitions>
/* Display properties from Work Item */
<TextBox Grid.Row="0" Grid.Column="0" Text="{Binding Code}" />
/* This SelectedItem works sometimes, but not all the time */
<ListBox Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" ItemsSource="{Binding Tasks}" SelectedItem="{Binding Path=DataContext.SelectedTask, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,0,0,5">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal">
/* Display properties from Work Item Task */
<TextBlock Text="{Binding Path=Description}" />
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
EDIT
I was asked what the SelectedTask property looked like:
public MyTaskType SelectedTask
{
get { return _selectedTask; }
set
{
_selectedTask = value;
NotifyPropertyChanged();
}
}

Related

WPF is rendering data very slowly

More than 8000 objects are getting binded to ListView control and a object has two properties.ie ID and Description).
LstViewTextBLocks is a Listview control whose itemsource is assigned to 'view' which is of type ICollectionView
Data gets loaded fast in codebehind(as noticed in debugging) but while rendering the data it takes almost 6 minutes and also when the data is rendered while scrolling through it becomes very laggy.
<TabItem Header="data" Background="Gray" Name="textPicker" >
<!--<local:TextPickerView/>-->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label DockPanel.Dock="Left" Content="Filter:" Grid.Column="0" Grid.Row="0"/>
<TextBox Name="TextblockFilter1" Grid.Row="0" Grid.Column="1"
Text="{Binding TextSearch,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" />
<ScrollViewer Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2"
VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<**ListView** Name="LstViewTextBLocks"
VirtualizingStackPanel.VirtualizationMode="Recycling" SelectionMode="Single"
VirtualizingPanel.IsVirtualizing="True" IsSynchronizedWithCurrentItem="True"
SelectedItem="{Binding SelectedItem,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}">
<ListView.View>
<GridView>
**<GridViewColumn Header="ID" Width="Auto" DisplayMemberBinding="{Binding ID}" />
<GridViewColumn Header="Description" DisplayMemberBinding="{Binding Description}" />**
</GridView>
</ListView.View>
</**ListView**>
</ScrollViewer>
</Grid>
</TabItem>
ItemsSource of my listview in the code behind was set like this:
this.LstViewTextBLocks.ItemsSource = this.View;
Remove the ScrollViewer from your code. This ScrollViewer allows for the ListView to occupy all the height it needs for your 8000+ rows. This results in removing the virtualization from the ListView. ListView has its own Scrollviewer which will be triggered in, and will also help in virtualization and fast performance comparatively.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label DockPanel.Dock="Left" Content="Filter:" Grid.Column="0" Grid.Row="0"/>
<TextBox Name="TextblockFilter1" Grid.Row="0" Grid.Column="1"
Text="{Binding TextSearch,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" />
<ListView Name="LstViewTextBLocks"
Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2"
VirtualizingStackPanel.VirtualizationMode="Recycling" SelectionMode="Single"
VirtualizingPanel.IsVirtualizing="True" IsSynchronizedWithCurrentItem="True"
SelectedItem="{Binding SelectedItem,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}">
<ListView.View>
<GridView>
<GridViewColumn Header="ID" Width="Auto" DisplayMemberBinding="{Binding ID}" />
<GridViewColumn Header="Description" DisplayMemberBinding="{Binding Description}" />
</GridView>
</ListView.View>
</ListView>
</Grid>

Display items in dropdown list and selected items box via the same binding using only XAML

I'm sure that someone will jump all over this as being answered somewhere else but I claim I have followed every relevant link on this site (and elsewhere) and tried all possible combinations of ItemTemplate, ComboBoxItem, ItemContainerStyle, DisplayMemberPath, SelectedItem, SelectedValuePath and SelectedValue and for the life of me I can't get this to work as desired.
I simply want to define a list of ContentControl object (using x:Array) in XAML resources used as an ItemsSource for a ComboBox. This list contains 2 elements whose Content property is set to a Grid control - also defined in the resources. Each item also has the Tag property set to a string that functions as the name or description of the content.
Very simply: I want the string in the Tag property to be displayed in the selected item portion of the ComboBox and as the list of selectable items in the dropdown portion of the ComboBox.
If I set DisplayMemberPath="Tag", then the dropdown portion works as desired but nothing is displayed in the selected item portion of the ComboBox. I'm sure that proper use of SelectedValuePath and SelectedValue is the key, but I simply can't get it to work.
<UserControl.Resources>
<Grid x:Key="Fluidics" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="134"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Cartridge:"
VerticalAlignment="Center" HorizontalAlignment="Left"
Margin="0,5,0,0" Height="26" Width="62"/>
<ComboBox Grid.Column="1"
ItemsSource="{Binding Cartridges}"
SelectedIndex="{Binding SelectedCartridgeIndex, FallbackValue=0}"
SelectedItem="{Binding SelectedCartridge}"
Margin="0,5,0,0" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Config.FriendlyName}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<views:FluidicsExperimentParamsView Grid.Row="1" Grid.ColumnSpan="2" DataContext="{Binding FluidicsExperimentParams}" Margin="0,10,0,0"/>
</Grid>
<Grid x:Key="Data acquisition" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="157"/>
<ColumnDefinition Width="Auto" MinWidth="87"/>
</Grid.ColumnDefinitions>
<Label Content="Camera line:" />
<ComboBox
Grid.Column="1"
IsReadOnly="True"
ItemsSource="{Binding ImageDataAcquisitionParams}"
SelectedIndex="{Binding SelectedCameraLine}"
SelectedItem="{Binding SelectedImageDataAcquisitionParams}"/>
<data:ImageDataAcquisitionView
Grid.Row="1"
Grid.ColumnSpan="2"
DataContext="{Binding ImageDataAcquisition}" Margin="0,10,0,0"/>
</Grid>
<x:Array x:Key="ControlActivities" Type="ContentControl">
<ContentControl Content="{Binding Source={StaticResource Fluidics}}" Tag="Fluidics" />
<ContentControl Content="{Binding Source={StaticResource Data acquisition}}" Tag="Data acquisition"/>
</x:Array>
</UserControl.Resources>
...
<ComboBox Name="ControlActivityComboBox"
MinWidth="150"
ItemsSource="{Binding Source={StaticResource ControlActivities}}"
SelectedItem="{Binding SelectedActivity}"
SelectedValuePath="Tag"
SelectedValue="{Binding SelectedActivity.Tag}"
DisplayMemberPath="Tag"
HorizontalAlignment="Left"
SelectedIndex="0">
</ComboBox>

user control inside tabitem does not change values

I have a TabContorl and I am adding tabitem dynamically. I have bind datatemplate to some viewmodel and view is loaded as defined in datatemplate.
<DataTemplate DataType="{x:Type viewmodels:ItemViewModel}">
<views:CustomerOrderView />
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:InvoiceGeneratorViewModel}">
<views:InvoiceGenerator/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:OrderDetailsViewModel}">
<views:OrderDetailsView/>
</DataTemplate>
if i use controls directly in customerorderview then it works fine. It means if i switch the tab all values are loaded accordingly but does not work properly when if i use another usercontrol insite customerview user control. I have created once user control have one textbox, button and a popup contorl. When i click on button the popup opens and remains even if swich the tab. If textbox value is change the value is also changed for all tabs. It looks one view is serving all the tabs. I could not understand how to handle it.
Below is the user control which i am using inside customer
<UserControl x:Class="OrderManagement.Views.ProductSelectionView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="35"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="30"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="txtProduct" Grid.Row="0" Grid.Column="0"/>
<Button x:Name="btnSearch" Content="S" Click="OnSearchClick" Grid.Row="0" Grid.Column="1" Margin="5"/>
<Popup x:Name="popup" PlacementTarget="{Binding ElementName=btnSearch}" Placement="Bottom"
Width="300" Height="300" Margin="5">
<Border BorderBrush="Black" BorderThickness="2" Background="AliceBlue">
<Grid >
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="35"/>
</Grid.RowDefinitions>
<DataGrid x:Name="dataGrid" AutoGenerateColumns="False" Grid.Row="0" Grid.ColumnSpan="2"
IsReadOnly="False" CanUserAddRows="False" CanUserDeleteRows="False">
<DataGrid.Columns>
<DataGridCheckBoxColumn Binding="{Binding IsChecked}" IsReadOnly="False"/>
<DataGridTextColumn Header="Product Name" Binding="{Binding Name}"/>
<DataGridTextColumn Header="Quantity In Stock" Binding="{Binding QuantityInStock}"/>
</DataGrid.Columns>
</DataGrid>
<WrapPanel Grid.Row="1" Grid.ColumnSpan="2" Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Right">
<Button Content="OK" Click="OnOKClick" Width="80"/>
<Button Content="Cancel" Click="OnCancelClick" Width="80" Margin="20,0,0,0"/>
</WrapPanel>
</Grid>
</Border>
</Popup>
</Grid>
</UserControl>
Tabcontrol virtualize the data as per its datacontext but popup and text value in productselection control remains the same for all tabs. opens the popup but it remains for all the tab.
Let me know if how it can be done or what is the mistake i am doing

How to setup a grid as template for an Items control?

I'm trying to create an ItemsControl that uses a grid as its ItemsPanel in such a way that it has two columns, where the first columns width is the width of the widest item in that column, and has as may rows needed to display all the items
Basically, I want the following, but somehow within an ItemsControl so that I can bind to a collection of objects:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label Content="{Binding Items[0].Header}"/>
<TextBox Text="{Binding Items[0].Content}" Grid.Column="1"/>
<Label Content="{Binding Items[1].Header}" Grid.Row="1"/>
<TextBox Text="{Binding Items[1].Content}" Grid.Row="1" Grid.Column="1"/>
<Label Content="{Binding Items[2].Header}" Grid.Row="2"/>
<TextBox Text="{Binding Items[2].Content}" Grid.Row="2" Grid.Column="1"/>
</Grid>
Edit : Rachels answer worked great, here is a working example.
(I moved the Grid.IsSharedSizeScope="True" to the ItemsPanel, not sure if Rachel meant to put it in the ItemTemplate (which didn't work))
namespace WpfApplication23
{
public partial class Window1 : Window
{
public List<Item> Items { get; set; }
public Window1()
{
Items = new List<Item>()
{
new Item(){ Header="Item0", Content="someVal" },
new Item(){ Header="Item1", Content="someVal" },
new Item(){ Header="Item267676", Content="someVal" },
new Item(){ Header="a", Content="someVal" },
new Item(){ Header="bbbbbbbbbbbbbbbbbbbbbbbbbb", Content="someVal" },
new Item(){ Header="ccccccc", Content="someVal" }
};
InitializeComponent();
DataContext = this;
}
}
public class Item
{
public string Header { get; set; }
public string Content { get; set; }
}
}
<Window x:Class="WpfApplication23.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Grid.IsSharedSizeScope="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="ColumnOne" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Content="{Binding Header}"/>
<TextBox Text="{Binding Content}" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
There are multiple problems here for an ItemsControl:
Getting your first column to match the width of the largest item
Generating a dynamic number of rows
Generating more than one item for each iteration of the ItemsControl
The last one is really the biggest problem, because an ItemsControl wraps each ItemTemplate in a ContentPresenter, so there is no default way of creating more than one item in the panel for each Iteration of the ItemsControl. Your end result would look like this:
<Grid>
...
<ContentPresenter>
<Label Content="{Binding Items[0].Header}"/>
<TextBox Text="{Binding Items[0].Content}" Grid.Column="1"/>
</ContentPresenter>
<ContentPresenter>
<Label Content="{Binding Items[1].Header}" Grid.Row="1"/>
<TextBox Text="{Binding Items[1].Content}" Grid.Row="1" Grid.Column="1"/>
</ContentPresenter>
<ContentPresenter>
<Label Content="{Binding Items[2].Header}" Grid.Row="2"/>
<TextBox Text="{Binding Items[2].Content}" Grid.Row="2" Grid.Column="1"/>
</ContentPresenter>
</Grid>
My best suggestion would be to create an ItemTemplate that contains a 1x2 Grid, and use Grid.IsSharedSizeScope to make the width of the first column shared. (The ItemsPanelTemplate would remain the default StackPanel.)
This way, the end result would look like this:
<StackPanel>
<ContentPresenter>
<Grid IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="ColumnOne" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Content="{Binding Header}"/>
<TextBox Text="{Binding Content}" Grid.Column="1"/>
</Grid>
</ContentPresenter>
<ContentPresenter>
<Grid IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="ColumnOne" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Content="{Binding Header}"/>
<TextBox Text="{Binding Content}" Grid.Column="1"/>
</Grid>
</ContentPresenter>
...
</StackPanel>
You can use a ListView
<ListView ItemsSource="{Binding MyList}">
<ListView.View>
<GridView>
<GridView.ColumnHeaderContainerStyle>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="Visibility" Value="Collapsed" />
</Style>
</GridView.ColumnHeaderContainerStyle>
<GridViewColumn
Header=""
Width="Auto"
DisplayMemberBinding="{Binding Header}"/>
<GridViewColumn
Header=""
DisplayMemberBinding="{Binding Value}"/>
</GridView>
</ListView.View>
</ListView>
the ColumnHeaderContainerStyle hides the GridViewHeader

Binding to property of DataTemplate of DataType

How can I bind to property of view in DataTemplate? Here is my code. You can see my attempt in MainWindow.xaml (Grid column 2, row 0) to bind to selected item in TreeView from column 0 - row - 0, but this does not work.
If I move TreeView to MainWindow.xaml instead of using DataTemplate - everything works as expected.
NOTE: Repository collection ({Binding Repositories} in MainWindow.xaml in column 0, row 0) is collection of RepositoryVM instances and its Items propery contains collection of ItemVM instances. Also, Children propery of ItemVM contains collections of ItemVM insances.
Code:
MainWindow.xaml
<Grid Margin="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="4" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Grid.Column="0" Grid.Row="0" MinWidth="200" >
<ContentControl Content="{Binding Repositories}"
ContentTemplate="{StaticResource WorkspaceTemplate}"/>
</Border>
<GridSplitter Grid.Column="1"
ResizeDirection="Columns"
ResizeBehavior="PreviousAndNext"
Width="4"
Height="Auto"/>
<Border Grid.Column="2" Grid.Row="0">
<ContentControl Content="{Binding ElementName=repoTree, Path=SelectedItem}"/>
</Border>
<StatusBar Grid.ColumnSpan="3" Grid.Column="0" Grid.Row="1">
<TextBlock Text="{Binding ElementName=repoTree, Path=SelectedItem}" />
</StatusBar>
</Grid>
MainWindowResources.xaml
<DataTemplate DataType="{x:Type vm:ItemVM}">
<vw:ItemView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:RepositoryVM}">
<vw:RepositoryView />
</DataTemplate>
<DataTemplate x:Key="WorkspaceTemplate">
<TabControl ItemsSource="{Binding}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}" />
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
</DataTemplate>
RepositoryView.xaml
<TreeView ItemsSource="{Binding Items}" x:Name="repoTree">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
ItemView.xaml
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0"
Grid.Row="0"
Text="Name: " />
<TextBlock Grid.Column="0"
Grid.Row="1"
Text="Type:" />
<TextBox Grid.Column="1"
Grid.Row="0"
Text="{Binding Name}"/>
<TextBox Grid.Column="1"
Grid.Row="1" />
</Grid>
Question is - how can I get my attempt of binding to repoTree to work and still use DataTemplate?
Update:
Error I get in output window is:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=repoTree'. BindingExpression:Path=SelectedItem; DataItem=null; target element is 'ContentControl' (Name=''); target property is 'Content' (type 'Object')
And the same one for statusbar, because it is the same binding.

Resources