How can I autoUpdate the content in ListBox - wpf

I am working on WPF Windows application. I am using ListBox to view/edit the content. In that I am doing calculation stuff. So I want that If I change the value of one Item it automatic change the calculation without adding extra buttons to regenerate it.
<ListBox ItemsSource="{Binding CustomSalesProducts, Mode=TwoWay}" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<Border>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsPresenter/>
</ScrollViewer>
</Border>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel CanHorizontallyScroll="True" CanVerticallyScroll="True" Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid x:Name="SalesGrid" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<controls:HeaderedContentControl Header="{Binding ProductName, Mode=TwoWay}" Margin="{DynamicResource Margin4}" Style="{DynamicResource HeaderedContentControlStyle}" HorizontalContentAlignment="Right">
</controls:HeaderedContentControl>
<TextBox Text="{Binding OrderQty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Row="1" Margin="{StaticResource Margin4}" Style="{DynamicResource MiniTextBoxStyle}" ToolTip="Quantity" />
<TextBlock Text="{Binding UnitSalePrice, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Column="1" Grid.Row="1" Margin="{StaticResource Margin4}" ToolTip="Price"/>
<TextBox Text="{Binding Discount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Column="2" Grid.Row="1" Margin="{StaticResource Margin4}" Style="{DynamicResource MiniTextBoxStyle}" ToolTip="Discount"/>
<TextBlock Text="{Binding TaxAmount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Column="3" Grid.Row="1" Margin="{StaticResource Margin4}" ToolTip="Tax Amount"/>
<TextBlock Text="{Binding LineTotal, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Column="4" Grid.Row="1" Margin="{StaticResource Margin4}" ToolTip="Total"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
Edit:
I tried trigger but it doesnt work in the listbox
like:
<TextBox Text="{Binding OrderQty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="{StaticResource Margin4}" Style="{DynamicResource MiniTextBoxStyle}" ToolTip="Quantity" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="LostFocus">
<i:InvokeCommandAction Command="{Binding RefreshProduct}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
Thanks

So this is what I suggest:
Make sure your Model class (e.g. CustomSalesProduct) is implementing INotifyPropertyChanged interface.
When your OrderQty property value changes (e.g. When user types another qty amount in TextBox) then call your method to calculate total in the Property Set. This will automatically refresh your LineTotal value in the View.
EDIT: If you decide to use the MVVM Design Pattern then:
Create a ViewModel class like:
public class CustomSalesViewModel
{
public ObservableCollection<CustomSalesProduct> CustomSalesProducts {get;set;}
public CustomSalesViewModel()
{
//Initialize your collection in constructor
CustomSalesProducts = new ObservableCollection<CustomSalesProduct>();
//Populate list
CustomSalesProducts.Add(new CustomSalesProduct(){....});
//.... Add rest of items
}
}
Then set the DataContext of your View to an instance of your CustomSalesViewModel.
You can do that in the Constructor of the code-behind of your View (XAML) like:
DataContext = new CustomSalesViewModel();
Here the sample class:
public class CustomSalesProduct : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _orderQty;
public int OrderQty
{
get { return _orderQty; }
set
{
_orderQty = value;
OnPropertyChanged("OrderQty");
CalcTotal();
}
}
private double _unitSalePrice;
public double UnitSalePrice
{
get { return _unitSalePrice; }
set
{
_unitSalePrice = value;
OnPropertyChanged("UnitSalePrice");
}
}
private double _discount;
public double Discount
{
get { return _discount; }
set
{
_discount = value;
OnPropertyChanged("Discount");
}
}
private double _taxAmount;
public double TaxAmount
{
get { return _taxAmount; }
set
{
_taxAmount = value;
OnPropertyChanged("TaxAmount");
}
}
private double _lineTotal;
public double LineTotal
{
get { return _lineTotal; }
set
{
_lineTotal = value;
OnPropertyChanged("LineTotal");
}
}
private string _productName;
public string ProductName
{
get { return _productName; }
set
{
_productName = value;
OnPropertyChanged("ProductName");
}
}
public void CalcTotal()
{
LineTotal = OrderQty*UnitSalePrice*(1 - Discount);
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}

Related

How do I get different colors for parts of string object property binded to label in ItemControl?

This is my object type:
public class selectedLevel
{
public string Level
{
get;
set;
}
public string PlanetSelected
{
get; set;
}
public string houseDetails
{
get;
set;
}
}
This is my itemTemplate:
<ItemsControl x:Name="LevelDetails" Margin="-464,416,120,-484" BorderBrush ="Black" ItemsSource="{Binding selectedLevels}" Grid.ColumnSpan="3" HorizontalContentAlignment="Stretch">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Col1" />
<ColumnDefinition SharedSizeGroup="Col2" />
<ColumnDefinition SharedSizeGroup="Col3" />
<ColumnDefinition SharedSizeGroup="Col4" />
<ColumnDefinition SharedSizeGroup="Col5" />
<ColumnDefinition SharedSizeGroup="Col6" />
<ColumnDefinition SharedSizeGroup="Col7" />
<ColumnDefinition SharedSizeGroup="Col8" />
<ColumnDefinition SharedSizeGroup="Col9" />
<ColumnDefinition SharedSizeGroup="Col10"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition SharedSizeGroup="Row1"/>
<RowDefinition SharedSizeGroup="Row2"/>
<RowDefinition SharedSizeGroup="Row3"/>
</Grid.RowDefinitions>
<Label x:Name="LevelName" Grid.Row="0" Content="{Binding Level}" HorizontalContentAlignment="Center" FontWeight="Bold" FontSize="16" BorderBrush="Black" BorderThickness="1" Background="{x:Null}"/>
<Label x:Name="PlanetName" Grid.Row="1" Content="{Binding PlanetSelected}" FontSize="14" Foreground="Red" BorderBrush="Black" FontWeight="Bold" HorizontalContentAlignment="Center" BorderThickness="1" />
<Label x:Name="LevelDetails" Grid.Row="2" HorizontalContentAlignment="Center" Content="{Binding houseDetails}" FontSize="14" BorderBrush="Black" FontWeight="Bold" BorderThickness="1" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The User control is bound with an object with an observable collection of type selectedLevel(shown at the top).
I am adding the properties of SelectedLevels in a view model as follows:
selectedLevels.Add(new selectedLevel
{
Level = "Mahadasha",
PlanetSelected = Mahadashas[0].rulerName,
houseDetails = indicesList[0] + ", " + indicesList[1] + ", " + indicesList[2]
});
I am trying to get different colors for indicesList[0], indicesList[1] and indicesList[2] in 3rd label which is bound with houseDetails but I can only change full text color and not a part of it.
I am totally lost here. Can I get some assistance on how to set different colors to parts of string?
It's simple, first you need to separate the houseDetails property into 3 strings, something similar to this:
public class selectedLevel
{
public string Level { get; set; }
public string PlanetSelected { get; set; }
public string houseDetails1 { get; set; }
public string houseDetails2 { get; set; }
public string houseDetails3 { get; set; }
}
Then you can use a TextBlock wrapped by a Border instead of the third Label and use Runs inside it to pick a different color for each one and bind them to the corresponding properties:
<Border HorizontalAlignment="Center" BorderBrush="Black" BorderThickness="1">
<TextBlock x:Name="LevelDetails" Grid.Row="2" FontSize="14" FontWeight="Bold">
<Run Text="{Binding houseDetails1}" Foreground="Red"/>,
<Run Text="{Binding houseDetails2}" Foreground="Green"/>,
<Run Text="{Binding houseDetails3}" Foreground="Blue"/>
</TextBlock>
</Border>
And then in the view model:
selectedLevels.Add(new selectedLevel
{
Level = "Mahadasha",
PlanetSelected = Mahadashas[0].rulerName,
houseDetails1 = indicesList[0],
houseDetails2 = indicesList[1],
houseDetails3 = indicesList[2]
});

Delete a subitem in a tree view with heirarchical data template

Please find below my XAML,
<loc:MultiSelectTreeView Height="295" ScrollViewer.VerticalScrollBarVisibility="Visible" BorderThickness="1" Background="WhiteSmoke" x:Name="GridListEmulation" Grid.Row="2" BorderBrush="Gray" ItemsSource="{Binding EmulationCollection,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" Margin="0,2,0,-2"
ItemContainerStyle="{StaticResource MultiSelectTreeViewItemStyle}" SelectedItemChanged="GridListEmulation_SelectedItemChanged">
<TreeView.ItemTemplate >
<HierarchicalDataTemplate ItemsSource="{Binding Items,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" >
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Stream" Width="60"/>
<ColumnDefinition SharedSizeGroup="Port" Width="55"/>
<ColumnDefinition SharedSizeGroup="Device Name" Width="100"/>
<ColumnDefinition SharedSizeGroup="Count" Width="50"/>
<ColumnDefinition SharedSizeGroup="FromMAC" Width="120"/>
<ColumnDefinition SharedSizeGroup="State" Width="60"/>
<ColumnDefinition SharedSizeGroup="MACAddress" Width="120"/>
<ColumnDefinition SharedSizeGroup="EmulationIPv4Address" Width="100"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding StreamId}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="1" Text="{Binding Port}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="2" Text="{Binding EmulationDeviceName}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="3" Text="{Binding SessionCount}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="4" Text="{Binding SourceMAC}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="5" Text="{Binding State}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="6" Text="{Binding SimulatedMAC}" Style="{StaticResource TextBlockStyle}"/>
<TextBlock Grid.Column="7" Text="{Binding EmulationIPv4Address}" Style="{StaticResource TextBlockStyle}"/>
</Grid>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</loc:MultiSelectTreeView>
I am trying to delete a subitem in this multi treeview which is bound to my observable collection as shown below,
tvm.EmulationCollection.RemoveAt(GridListEmulation.Items.IndexOf(subItem));
But i always get the index as -1 for subitem and then gives an exception. Please let me know if any way to get the subitem in the tree view item of the heirarchical data template and delete it? Thanks in advance.
here is a basic example of using a VM to host a tree
public class TreeVM : BindableBase
{
public TreeVM()
{
AddChild = new DelegateCommand(() => Items.Add(new TreeVM() {Parent = this }));
RemoveMe = new DelegateCommand(() => Parent.Items.Remove(this));
}
private string _Text;
public string Text
{
get { return _Text; }
set { SetProperty(ref _Text, value); }
}
private TreeVM _Parent;
public TreeVM Parent
{
get { return _Parent; }
set { SetProperty(ref _Parent, value); }
}
public ObservableCollection<TreeVM> Items { get; } = new ObservableCollection<TreeVM>();
public DelegateCommand AddChild { get; set; }
public DelegateCommand RemoveMe { get; set; }
}
then hosted on this XAML
<StackPanel>
<Button Content="Add" Command="{Binding AddChild}"/>
<TreeView ItemsSource="{Binding Items}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Items}">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Text}"/>
<Button Content="Add" Command="{Binding AddChild}"/>
<Button Content="Delete" Command="{Binding RemoveMe}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</StackPanel>
as you can see the child is responsible for removing itself from the tree, and this works because your VM knows its parent as well as its children

WPF MVVM ListBox does not display last record

I find the problem I have very strange.
I have a WPF application using MVVM pattern.
I retrieve data from my database using Linq-to-SQL and display the Description field data in a ListBox using binding.
It works, but not completely. For some reason the last record's does not display. I have verified it is fetched from the database. It cannot be selected; its not there at all.
The only way to get the record to appear is by setting focus to the ListBox and either scrolling the mouse scroller or actually moving down using the keyboard to past the last record (which is supposed to be the second last record). Once you do that, the record appears.
Another way to get the record to show up is update any of the records in the ListBox. I have a TextBox bound in two-way mode to the SelectedItem of the ListBox, So when I select an item, change the text in the TextBox and click the Update button, it calls the Command to do the database update, which works fine, but then also the missing record of the last record shows up.
Is there a rendering problem with ListBox and ListView, because I have tried both? Why does it seem like the ListBox needs to be "redrawn" before the last items appears?
CategoryModel.cs
public class CategoryModel
{
public int CategoryID { get; set; }
public string Description { get; set; }
public List<CategoryModel> categories = new List<CategoryModel>();
readonly SalesLinkerDataContext _dbContext = new SalesLinkerDataContext();
public void GetCategories()
{
categories.Clear();
var result = _dbContext.tblSalesCategories.ToList();
foreach (var item in result)
{
categories.Add(new CategoryModel
{
CategoryID = item.CategoryID,
Description = item.Description.Trim()
});
}
}
internal void Update(CategoryModel cm)
{
try
{
var category = (from a in _dbContext.tblSalesCategories
where a.CategoryID == cm.CategoryID
select a).FirstOrDefault();
if (category != null)
{
category.Description = cm.Description;
_dbContext.SubmitChanges();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
CategoriesViewModel.cs
public class CategoriesViewModel : ViewModelBase, IPageViewModel
{
public CategoryModel Categories = new CategoryModel();
private DelegateCommand _getCategoriesCommand;
private DelegateCommand _updateCategoryCommand;
/// <summary>
/// Describes the name that will be used for the menu option
/// </summary>
public string Name
{
get { return "Manage Categories"; }
}
public string Description
{
get { return Categories.Description; }
set
{
Categories.Description = value;
OnPropertyChanged("Description");
}
}
public List<CategoryModel> ReceivedCategories
{
get { return Categories.categories; }
set
{
Categories.categories = value;
OnPropertyChanged("ReceivedCategories");
}
}
public ICommand GetCategoriesCommand
{
get
{
if (_getCategoriesCommand == null)
{
_getCategoriesCommand = new DelegateCommand(GetCategories, CanGetCategories);
}
return _getCategoriesCommand;
}
}
private bool CanGetCategories()
{
return true;
}
private void GetCategories()
{
Categories.GetCategories();
ReceivedCategories = Categories.categories;
}
public ICommand UpdateCategoryCommand
{
get
{
if (_updateCategoryCommand == null)
{
_updateCategoryCommand = new DelegateCommand(UpdateCategory, CanUpdateCategory);
}
return _updateCategoryCommand;
}
}
private bool CanUpdateCategory()
{
return true;
}
private void UpdateCategory()
{
Categories.Update(Categories);
}
}
CategoriesView.xaml
<UserControl x:Class="SalesLinker.CategoriesView"
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"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="600" Background="White" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding GetCategoriesCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="45"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Margin="20,0,0,0" FontSize="20" HorizontalAlignment="Center" Content="Categories"/>
<ListView x:Name="LstCategories" ItemsSource="{Binding ReceivedCategories, Mode=TwoWay}" Grid.Column="0" Grid.Row="1"
VerticalAlignment="Stretch"
Background="LightGray"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
SelectionChanged="LstCategories_OnSelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Stretch" >
<TextBlock Text="{Binding Description }"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Command="{Binding AddCategoryCommand}" Grid.Column="1" Grid.Row="1" VerticalAlignment="Top" Height="50" Width="50" Margin="0,20,0,0" Background="Transparent" BorderThickness="0" BorderBrush="Transparent" >
<Image Source="/Images/Plus.png"/>
</Button>
<Button Command="{Binding RemoveCategoryCommand}" Grid.Column="1" Grid.Row="1" VerticalAlignment="Top" Height="50" Width="50" Margin="0,75,0,0" Background="Transparent" BorderThickness="0">
<Image Source="/Images/Minus.png"/>
</Button>
<Grid Grid.Row="1" Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Row="0" Grid.Column="0" Content="Description:"/>
<TextBox DataContext="CategoryModel" Grid.Row="0" Grid.Column="1" Width="250" Height="Auto" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0"
Text="{Binding SelectedItem.Description, ElementName=LstCategories}"/>
<Button Command="{Binding UpdateCategoryCommand}"
Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" Margin="10,0,0,0" Height="20" Width="120" Content="Update Description"/>
</Grid>
</Grid>
I am very new to MVVM so hopefully I am just not seeing something simple.
For some reason the last record's does not display. I have verified it is fetched from the database.
If your fetch all data from database, then it can be concluded that why you cannot see your last item is as you use using not correct layout. I mean static layout.
I suggest you to use dynamic layout, not static. I mean it is bad:
<Grid.RowDefinitions>
<RowDefinition Height="45"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
The following code is better and it affords you to see all items to be seen and to be resized accoriding the display and Width and Height of your Window or UserControl:
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
Let me show the full example:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Margin="20,0,0,0" FontSize="20" HorizontalAlignment="Center" Content="Categories"/>
<ListView x:Name="LstCategories" ItemsSource="{Binding ReceivedCategories, Mode=TwoWay}" Grid.Column="0" Grid.Row="1"
VerticalAlignment="Stretch"
Background="LightGray"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Stretch" >
<TextBlock Text="{Binding Description }"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Command="{Binding AddCategoryCommand}" Grid.Column="1" Grid.Row="1" VerticalAlignment="Top" Height="50" Width="50" Margin="0,20,0,0" Background="Transparent" BorderThickness="0" BorderBrush="Transparent" >
<Image Source="/Images/Plus.png"/>
</Button>
<Button Command="{Binding RemoveCategoryCommand}" Grid.Column="1" Grid.Row="1" VerticalAlignment="Top" Height="50" Width="50" Margin="0,75,0,0" Background="Transparent" BorderThickness="0">
<Image Source="/Images/Minus.png"/>
</Button>
<Grid Grid.Row="1" Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1.5*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Label VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Row="0" Grid.Column="0" Content="Description:"/>
<TextBox DataContext="CategoryModel" Grid.Row="0" Grid.Column="1" Height="Auto" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="10,0,0,0"
Text="{Binding SelectedItem.Description, ElementName=LstCategories}"/>
<Button Command="{Binding UpdateCategoryCommand}"
Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" Content="Update Description"/>
</Grid>
Update:
In order to dispel any doubts, I've made a test to show that all items are shown:
I've made some test model class:
public class FooClass
{
public string Description { get; set; }
}
And I've populated your ListView in a loop:
public MainWindow()
{
InitializeComponent();
PopulateCollection();
}
private void PopulateCollection()
{
List<FooClass> fooColl = new List<FooClass>();
for (int i = 0; i <= 1000; i++)
{
fooColl.Add(new FooClass() { Description=i.ToString()});
}
LstCategories.ItemsSource = fooColl;
}
XAML:
<ListView x:Name="LstCategories" Grid.Column="0" Grid.Row="1"
VerticalAlignment="Stretch"
Background="LightGray"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Stretch" >
<TextBlock Text="{Binding Description }"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The result is:

How can I get SelectedItem and show Headers using ItemsControl?

I am working on WPF Windows Application. I'm using ItemsControl to show collection list. Working on this I found there is no SelectedItem property in ItemsControl. Then how can I get the Selected Item from the ItemsControl. And also How can I display the Headers of ItemsControl.
<ItemsControl ItemsSource="{Binding CustomSalesProducts, Mode=TwoWay}">
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<Border>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsPresenter/>
</ScrollViewer>
</Border>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel CanHorizontallyScroll="True" CanVerticallyScroll="True" Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid x:Name="SalesGrid" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<controls:HeaderedContentControl Header="{Binding ProductName, Mode=TwoWay}" Margin="{DynamicResource Margin4}" Style="{DynamicResource HeaderedContentControlStyle}" HorizontalContentAlignment="Right">
</controls:HeaderedContentControl>
<TextBox Text="{Binding OrderQty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Row="1" Margin="{StaticResource Margin4}" Style="{DynamicResource MiniTextBoxStyle}" ToolTip="Quantity" />
<TextBlock Text="{Binding UnitSalePrice, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Column="1" Grid.Row="1" Margin="{StaticResource Margin4}" ToolTip="Price"/>
<TextBox Text="{Binding Discount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Column="2" Grid.Row="1" Margin="{StaticResource Margin4}" Style="{DynamicResource MiniTextBoxStyle}" ToolTip="Discount"/>
<TextBlock Text="{Binding TaxAmount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Column="3" Grid.Row="1" Margin="{StaticResource Margin4}" ToolTip="Tax Amount"/>
<TextBlock Text="{Binding LineTotal, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Column="4" Grid.Row="1" Margin="{StaticResource Margin4}" ToolTip="Total"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Thanks,
As you said there is no SelectedItem in the ItemsControl. You can use ListBox instead.
A bit late to the party, but I came across the same problem, and in the hopes that this can help someone else here's how I rolled my own SelectedItem since I didn't want to use a ListBox.
You could expose a SelectedCustomSalesProduct property to the class you are using as your DataContext, and then you can keep track of the selected item yourself by setting it when the item is selected.
In your SalesGrid, you could add event handlers for the MouseLeftButtonDown and for the TouchDown events, and use the Tag property to keep a reference to the item being rendered as such:
Please note that in my case, I was using a StackPanel instead of a Grid, and I didn't compile the code below, use it for illustrative purposes.
By using this example you should be able to get the general idea and set the Selected item in your business service.
<DataTemplate>
<Grid x:Name="SalesGrid" Background="White"
Tag="{Binding}"
TouchDown="DataTemplate_Touch"
MouseLeftButtonDown="DataTemplate_Click">
Then in your UserControl/window's code behind you can keep track of the selected item as such:
/// <summary>
/// MyScreen.xaml
/// </summary>
public partial class MyScreen : UserControl
{
private MyServiceWrapper _serviceWrapper;
public MyScreen()
{
InitializeComponent();
}
public MyScreen(MyServiceWrapper serviceWrapper)
{
//Instrumentation.Log(typeof(MyScreen), LogTypes.Trace, "Creating instance of MyScreen");
this._serviceWrapper = serviceWrapper;
// Set your DataContext, is this the class that would also have your
// CustomSalesProducts property exposed
this.DataContext = this._serviceWrapper;
InitializeComponent();
}
private void DataTemplate_Touch(object sender, System.Windows.Input.TouchEventArgs e)
{
SetSelectedCustomSalesProduct(sender);
}
private void DataTemplate_Click(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
SetSelectedCustomSalesProduct(sender);
}
private void SetSelectedCustomSalesProduct(object sender)
{
_serviceWrapper.SelectedCustomSalesProduct = ((Grid)sender).Tag as CustomSalesProduct;
}
}
I found that for using headers there is HeaderdItemsControl. With this I can add headers and also it is not repeatable. But problem with this is that we have to define static size for header and its item if we define auto size then the UI of headeredItemsControl is not perfect so we have to give its static size.
You can read this for how to use HeaderedItemsControl?

WPF Binding to child of current item not updating

I am currently binding to an ObservableCollection using an ICollectionView, myCollectionView. The contents of that collection are being selected from a ComboBox. Each collection item, myCollectionItem, has a VisualBrush, myVisualBrush, as a child and the CurrentItem's brush is displayed in a preview panel.
The collection item also a child object, myItemChild, which contains a number of its own properties that are used to generate a slider. This slider alters properties on the preview panel.
This all works as expected.
When the CurrentItem of the Collectionview is changed the preview panel updates correctly but the slider continues to show the previous CurrentItem's myItemChild.
The change to myItemChild is not being raised, how should I handle this situation?
Its highly probable I have missed something obvious so any pointers appreciated.
Regards
Rob
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<!-- Combo Box for selection of item-->
<ComboBox Grid.Row="0" ItemsSource="{Binding myCollectionView, Mode=TwoWay}" IsSynchronizedWithCurrentItem="True">
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type vm:myCollectionItem}" >
<StackPanel>
<Rectangle Height="40" Width="40" Fill="{Binding myVisualBrush}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<!-- Panel to preview item-->
<ContentControl Grid.Row="1" Content="{Binding myCollectionView/}">
<ContentControl.ContentTemplate>
<DataTemplate DataType="{x:Type vm:myCollectionItem}" >
<Rectangle Margin="20" Fill="{Binding myVisualBrush}" />
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
<!-- Slider to edit item-->
<ContentControl Grid.Row="2" Content="{Binding myCollectionView/}">
<ContentControl.ContentTemplate>
<DataTemplate DataType="{x:Type vm:myCollectionItem}" >
<ContentControl Content="{Binding myItemChild}">
<ContentControl.ContentTemplate>
<DataTemplate DataType="{x:Type vm:myCollectionItemChild}" >
<StackPanel>
<Label Content="{Binding myValueLabel, Mode=OneWay}"/>
<Slider Value="{Binding myValue, Mode=TwoWay}" Maximum="{Binding myValueMax}" Minimum="{Binding myValueMin}"/>
</StackPanel>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>
I tried to reproduce your problem, but it works without problems.
Here is my code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var items = new ObservableCollection<myCollectionItem>{
new myCollectionItem(Brushes.Red, new myCollectionItemChild("Red", 15, 0, 80)),
new myCollectionItem(Brushes.Green, new myCollectionItemChild("Green", 0.7, 0, 1)),
new myCollectionItem(Brushes.Purple, new myCollectionItemChild("Purple", 22,11,33))};
this.DataContext = new Model { myCollectionView = items };
}
}
public class Model
{
public ObservableCollection<myCollectionItem> myCollectionView { get; set; }
}
public class myCollectionItem
{
public myCollectionItem(Brush br, myCollectionItemChild child)
{
this.myVisualBrush = br;
this.myItemChild = child;
}
public Brush myVisualBrush { get; set; }
public myCollectionItemChild myItemChild { get; set; }
}
public class myCollectionItemChild
{
public myCollectionItemChild(string label, double val, double min, double max)
{
this.myValueLabel = label;
this.myValue = val;
this.myValueMin = min;
this.myValueMax = max;
}
public string myValueLabel { get; set; }
public double myValue { get; set; }
public double myValueMax { get; set; }
public double myValueMin { get; set; }
}
Also, you don't need to use control templates. It can be written more clearly:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="80"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<!-- Combo Box for selection of item-->
<ComboBox Grid.Row="0" ItemsSource="{Binding myCollectionView, Mode=TwoWay}" IsSynchronizedWithCurrentItem="True">
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type vm:myCollectionItem}" >
<StackPanel>
<Rectangle Height="40" Width="40" Fill="{Binding myVisualBrush}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<!-- Panel to preview item-->
<Rectangle Margin="20" Fill="{Binding myCollectionView/myVisualBrush}" Grid.Row="1" />
<!-- Slider to edit item-->
<StackPanel Grid.Row="2" DataContext="{Binding myCollectionView/myItemChild}">
<Label Content="{Binding myValueLabel, Mode=OneWay}"/>
<Slider Value="{Binding myValue, Mode=TwoWay}" Maximum="{Binding myValueMax}" Minimum="{Binding myValueMin}"/>
</StackPanel>
</Grid>

Resources