How to collapse child control in listviewitem UWP - c#

Universal Windows Platform, C#
How can I collapse/expand the child listview of item MainListView listitem from code behind? I have not found anything that works.
I'd like to do this on the SelectionChanged event.
XAML
<ListView x:Name="DestListView" SelectionMode="Single" Margin="0,60,0,0" SelectionChanged="listview_SelectionChanged" >
<ListView.ItemContainerStyle >
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="BorderThickness" Value="0,.5,0,0" />
<Setter Property="BorderBrush" Value="Gainsboro" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate >
<DataTemplate>
<StackPanel>
<Grid>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<CheckBox Grid.Column="0" MinWidth="20" />
<TextBlock Grid.Column="1" Text="{Binding destination}" FontSize="20" />
<TextBlock Grid.Column="2" Text="{Binding total_quantity}" FontSize="20" Margin="10,0,0,0"/>
<TextBlock Grid.Column="3" Text="{Binding package_type}" FontSize="20" HorizontalAlignment="Center" Margin="10,0,0,0"/>
<TextBlock Grid.Column="4" Text="{Binding total_weight}" FontSize="20" Margin="10,0,0,0"/>
</Grid>
</Grid>
**<!--Collpase/Expand-->**
<ListView x:Name="DetailListView" ItemsSource="{Binding destination_data}" SelectionMode="Multiple" Margin="20,0,0,0" Visibility="Collapsed" >
<ListView.ItemTemplate >
<DataTemplate >
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding visual_number}" FontSize="14" Foreground="White" HorizontalAlignment="Stretch" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
CodeBehind
private void listview_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
//GET THE ITEM
var selectItem = DestListView.Items[DestListView.SelectedIndex];
//GET THE CHILD SOMEHOW
//ListView childListView = (ListView)...not sure what to do here
//if (childListView != null)
//{
// if (childListView.Visibility == Visibility.Collapsed)
// {
// //childListView.Visibility = Visibility.Collapsed;
// }
// else
// {
// //childListView.Visibility = new Visibility;
// }
//}
}

Here is how i would do it.
I would create two DataTemplates one to show when selected (Expanded), another when not expanded.
Below is my ViewModel.
public class MyViewModel
{
public MyViewModel()
{
myItems = new ObservableCollection<MyItems>();
for(int i=1;i<=10;i++)
{
MyItems item = new MyItems();
item.Name = "Main Item " + i.ToString();
ObservableCollection<MySubItems> subItems = new ObservableCollection<MySubItems>();
for (int j=1;j<=5;j++)
{
subItems.Add(new MySubItems() { Title = "Sub Item " + j.ToString() });
}
item.Data = subItems;
myItems.Add(item);
}
}
public ObservableCollection<MyItems> myItems { get; set; }
}
public class MyItems
{
public string Name { get; set; }
public ObservableCollection<MySubItems> Data { get; set; }
}
public class MySubItems
{
public string Title { get; set; }
}
Below is my MainPage.xaml as requested
<Page
x:Class="App2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App2"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.DataContext>
<local:MyViewModel/>
</Page.DataContext>
<Page.Resources>
<DataTemplate x:Key="NoSelectDataTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Name}" />
</Grid>
</DataTemplate>
<DataTemplate x:Key="SelectDataTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Name}" />
<ListView ItemsSource="{Binding Data}" Grid.Row="1">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="{Binding myItems}" SelectionChanged="ListView_SelectionChanged" ItemTemplate="{StaticResource NoSelectDataTemplate}">
<ListView.ItemContainerStyle >
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="BorderThickness" Value="0,.5,0,0" />
<Setter Property="BorderBrush" Value="Gainsboro" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
</Grid>
</Page>
And Below is how my SelectionChanged Event Looks like.
private int PreviousIndex;
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListView lv = sender as ListView;
if (PreviousIndex >=0)
{
ListViewItem prevItem = (lv.ContainerFromIndex(PreviousIndex)) as ListViewItem;
prevItem.ContentTemplate = Resources["NoSelectDataTemplate"] as DataTemplate;
}
ListViewItem item = (lv.ContainerFromIndex(lv.SelectedIndex)) as ListViewItem;
item.ContentTemplate = Resources["SelectDataTemplate"] as DataTemplate;
PreviousIndex = lv.SelectedIndex;
}
Here is the Output

Related

UWP MasterDetail control UserControl dependancy property

I'm working on a master detail page created by the Windows Template Studio. I'm trying to have a comboBox in the userControl that defines the DetailsTemplate, which loads its values from a list in the page.
Here's the code:
ArticlesPage.xaml
<Page
x:Class="Estimates.Views.ArticlesPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Style="{StaticResource PageStyle}"
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
xmlns:model="using:Estimates.Models"
xmlns:views="using:Estimates.Views"
xmlns:fcu ="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract,5)"
xmlns:cu ="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractNotPresent(Windows.Foundation.UniversalApiContract,5)"
mc:Ignorable="d">
<Page.Transitions>
<TransitionCollection>
<NavigationThemeTransition />
</TransitionCollection>
</Page.Transitions>
<Page.Resources>
<DataTemplate x:Key="ItemTemplate" x:DataType="model:Article">
<Grid Height="64" Padding="0,8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="1" Margin="12,0,0,0" VerticalAlignment="Center">
<TextBlock Text="{x:Bind Description, Mode=TwoWay}" Style="{ThemeResource ListTitleStyle}"/>
</StackPanel>
</Grid>
</DataTemplate>
<DataTemplate x:Key="DetailsTemplate" x:DataType="views:ArticlesPage">
<views:ArticlesDetailControl MasterMenuItem="{Binding}" MeasureUnits="{x:Bind MeasureUnits}" />
</DataTemplate>
<DataTemplate x:Key="NoSelectionContentTemplate">
<TextBlock x:Uid="Articles_NoSelection" Style="{StaticResource ListNoSelectionTextStyle}" />
</DataTemplate>
</Page.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition x:Name="TitleRow" Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock
x:Uid="Articles_Title"
x:Name="TitlePage"
Margin="12,0,12,7"
Style="{StaticResource PageTitleStyle}" />
<controls:MasterDetailsView
Grid.Row="1"
x:Name="MasterDetailsViewControl"
ItemsSource="{x:Bind Items}"
SelectedItem="{x:Bind Selected, Mode=TwoWay}"
ItemTemplate="{StaticResource ItemTemplate}"
NoSelectionContentTemplate="{StaticResource NoSelectionContentTemplate}"
BorderBrush="Transparent"
DetailsTemplate="{StaticResource DetailsTemplate}" >
</controls:MasterDetailsView>
<Grid VerticalAlignment="Bottom" HorizontalAlignment="Right" Padding="0,15,0,0" Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button x:Uid="AddButton" Grid.Column="0" Margin="5" Click="AddButton"/>
<Button x:Uid="SaveButton" Grid.Column="1" Margin="5" Click="SaveButton"/>
<Button x:Uid="DeleteButton" Grid.Column="2" Margin="5" Click="DeleteButton"/>
</Grid>
<!-- Adaptive triggers -->
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="WindowStates">
<VisualState x:Name="WideState">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="640"/>
</VisualState.StateTriggers>
</VisualState>
<VisualState x:Name="NarrowState">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="TitlePage.Margin" cu:Value="60,0,12,7" fcu:Value="12,0,12,7"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
ArticlesPage.xaml.cs
public sealed partial class ArticlesPage : Page, INotifyPropertyChanged
{
private Article _selected;
private ObservableCollection<MeasureUnit> _measureUnits;
private IRepositoryService _repositoryService { get; set; }
public Article Selected
{
get { return _selected; }
set { Set(ref _selected, value); }
}
public ObservableCollection<MeasureUnit> MeasureUnits
{
get { return _measureUnits; }
set { Set(ref _measureUnits, value);}
}
public ObservableCollection<Article> Items { get; private set; } = new ObservableCollection<Article>();
public ArticlesPage()
{
InitializeComponent();
Loaded += ArticlesPage_Loaded;
_repositoryService = new RepositoryService();
}
private void ArticlesPage_Loaded(object sender, RoutedEventArgs e)
{
Items.Clear();
ArticledDetailControl.xaml
<UserControl
x:Class="Estimates.Views.ArticlesDetailControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:model="using:Estimates.Models"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<ScrollViewer Name="ForegroundElement" VerticalScrollMode="Enabled" HorizontalAlignment="Stretch" Padding="12,0">
<StackPanel HorizontalAlignment="Stretch">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch" Margin="0,8,0,0">
<TextBlock
Margin="12,0,0,0"
Text="{x:Bind MasterMenuItem.Description, Mode=TwoWay}"
Style="{StaticResource SubheaderTextBlockStyle}" />
</StackPanel>
<StackPanel Name="block" Padding="0,15,0,0">
<TextBox x:Uid="Description" Text="{x:Bind MasterMenuItem.Description, Mode=TwoWay}" />
<TextBox x:Uid="Notes" Text="{x:Bind MasterMenuItem.Notes, Mode=TwoWay}" Margin="0,6,0,0"/>
<Grid Margin="0,6,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.33*" />
<ColumnDefinition Width="0.33*" />
<ColumnDefinition Width="0.33*" />
</Grid.ColumnDefinitions>
<ComboBox x:Uid="MeasureUnits" x:Name="MeasureUnitsCB" ItemsSource="{x:Bind MeasureUnits}" Grid.Column="0">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="model:MeasureUnit">
<TextBlock Text="{x:Bind Description}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBox x:Uid="Price" Text="{x:Bind MasterMenuItem.Price, Mode=TwoWay}" Grid.Column="2" />
</Grid>
</StackPanel>
</StackPanel>
</ScrollViewer>
ArticlesDetailControl.xaml.cs
public sealed partial class ArticlesDetailControl : UserControl
{
public Article MasterMenuItem
{
get { return GetValue(MasterMenuItemProperty) as Article; }
set { SetValue(MasterMenuItemProperty, value); }
}
public IEnumerable<MeasureUnit> MeasureUnits
{
get { return GetValue(MeasureUnitsProperty) as IEnumerable<MeasureUnit>; }
set { SetValue(MeasureUnitsProperty, value); }
}
public static readonly DependencyProperty MasterMenuItemProperty =
DependencyProperty.Register("MasterMenuItem", typeof(Article), typeof(ArticlesDetailControl), new PropertyMetadata(null, OnMasterMenuItemPropertyChanged));
public static readonly DependencyProperty MeasureUnitsProperty =
DependencyProperty.Register("MeasureUnits", typeof(IEnumerable<MeasureUnit>), typeof(ArticlesDetailControl), new PropertyMetadata(null, OnMeasureUnitsPropertyChanged));
public ArticlesDetailControl()
{
InitializeComponent();
}
private static void OnMasterMenuItemPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as ArticlesDetailControl;
control.ForegroundElement.ChangeView(0, 0, 1);
}
private static void OnMeasureUnitsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Debug.WriteLine("PD");
}
}
So, the question is: How MasterMenuItem is populated? And why my property MeasureUnits it's not populated in the UserControl?
Edit: As suggested by TheZapper in the accepted answer, here's the implementation of OnMenuMasterItemPropertyChanged:
private static void OnMasterMenuItemPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as ArticlesDetailControl;
control.ForegroundElement.ChangeView(0, 0, 1);
if (control.MasterMenuItem.MeasureUnit != null)
{
control.MeasureUnitsCB.SelectedItem =
control.MeasureUnits.First(s => s.Id == control.MasterMenuItem.MeasureUnit.Id);
}
if (control.MasterMenuItem.VatCode != null)
{
control.VatCodesCB.SelectedItem =
control.VatCodes.First(s => s.Id == control.MasterMenuItem.VatCode.Id);
}
}
I load the lists in the constructor of the UserControl.
I guess your problem results from binding to MasterMenuItemProperty in your UserControl
Maybe you could add DependencyProperties as Notes, Description in your user control and define your Template like this
<DataTemplate x:Key="DetailsTemplate" x:DataType="views:ArticlesPage">
<views:ArticlesDetailControl Notes="{x:Bind Notes}" MeasureUnits="{x:Bind MeasureUnits}" />
</DataTemplate>
or you could try to update your bindings in the Changed-Event.
private static void OnMasterMenuItemPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as ArticlesDetailControl;
control.ForegroundElement.ChangeView(0, 0, 1);
*** Update Binding ***
}

Access items inside Listview

I have a Listview inside a Listview as you can see in my code. I am trying to collapse other categories when i open another. Is that possible? I have tried many things but I don't know how to access elements in other row...
<ListView x:Name="MainListView"
ItemsSource="{x:Bind menu}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment"
Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:MainCategories">
<Grid Background="blue">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock x:Name="test"
Text="{x:Bind CategoryName}"
Tapped="Category_TextBlock_Tapped"
FontSize="25" />
<Grid Grid.Row="1"
Name="tittleGrid"
Background="Gray">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<TextBlock Text="Name"
HorizontalAlignment="Center" />
<Border BorderBrush="Black"
BorderThickness="1,0,1,0"
Grid.Column="1">
<TextBlock HorizontalAlignment="Center"
Text="Price" />
</Border>
<TextBlock Text="QUantity"
Grid.Column="2"
HorizontalAlignment="Center" />
</Grid>
<ListView x:Name="SubListView"
Grid.Row="2"
Background="YellowGreen"
ItemsSource="{x:Bind SubMenuItems}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment"
Value="Stretch" />
<Setter Property="Padding"
Value="0" />
<Setter Property="VerticalContentAlignment"
Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:Dishes">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<TextBlock VerticalAlignment="Center"
Text="{x:Bind dishName}"
HorizontalAlignment="Center" />
<Border BorderBrush="Black"
BorderThickness="1,0,1,0"
Grid.Column="1">
<TextBlock VerticalAlignment="Center"
Grid.Column="1"
HorizontalAlignment="Center"
Text="{x:Bind dishPrice}" />
</Border>
<TextBlock Grid.Column="2"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Text="{x:Bind dishPrice}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And here is the toggle-visibility method i have made and works fine for the one i am clicking but I want to collapse ALL the others when i expand the one i click... I might have many mistakes in my code but I'm kinda new to UWP
private void Category_TextBlock_Tapped(Object sender, TappedRoutedEventArgs e)
{
CloseAllOthers();
TextBlock categoryName = sender as TextBlock;
Grid grid = (categoryName.Parent as Grid);
ToggleVisibility(grid);
}
private void ToggleVisibility(Grid grid)
{
foreach (var gr in grid.Children)
{
if (gr.GetType() == grid.GetType() || gr.GetType() == MainListView.GetType())
{
if (gr.Visibility == Visibility.Visible)
{
gr.Visibility = Visibility.Collapsed;
}
else
gr.Visibility = Visibility.Visible;
}
}
}
My result so far
and the collapsed version
Here's a simplified example of what you want to do:
<ListView x:Name="menu" ItemsSource="{x:Bind MenuItems}" IsItemClickEnabled="True" ItemClick="onItemClick">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<!-- Disable virtualization -->
<StackPanel/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.Resources>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="MinHeight" Value="0"/>
</Style>
</ListView.Resources>
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:MenuItem">
<StackPanel>
<TextBlock Text="{x:Bind Text}" FontWeight="Bold" Margin="10,10,0,10"/>
<ListView x:Name="subMenu" ItemsSource="{x:Bind SubItems}" Visibility="Collapsed">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:MenuItem">
<TextBlock Margin="20,5,0,5" Text="{x:Bind Text}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
public class MenuItem
{
public string Text { get; set; }
public List<MenuItem> SubItems { get; set; }
}
public sealed partial class MainPage : Page
{
public List<MenuItem> MenuItems { get; }
public MainPage()
{
InitializeComponent();
MenuItems = Enumerable.Range(1, 3).Select(i => new MenuItem()
{
Text = $"Menu item {i}",
SubItems = Enumerable.Range(1, 3).Select(j => new MenuItem()
{
Text = $"Sub item {i}.{j}",
}).ToList(),
}).ToList();
}
private void onItemClick(object sender, ItemClickEventArgs e)
{
foreach (var item in menu.Items)
{
var container = (ListViewItem)menu.ContainerFromItem(item);
if (container != null)
{
var subMenu = (container.ContentTemplateRoot as FrameworkElement)?.FindName("subMenu") as FrameworkElement;
if (subMenu != null)
{
subMenu.Visibility = e.ClickedItem == item ? Visibility.Visible : Visibility.Collapsed;
}
}
}
}
}
I wouldn't necessarily recommend this approach; I prefer more of a data-driven approach with bindings to control the visibility of the sub menus when the parent list item is selected, rather than accessing the view directly, but this is fine for simple scenarios.

Windows UWP - How to programmatically scroll ListView in ContentTemplate

I have a list of chats on the left and messages for a given chat on the right.
I want to have the MessageList to scroll to the bottom whenever it appears or gets its data updated. How can I do this?
My code is based off of Microsoft's Master/Detail view example:
https://github.com/Microsoft/Windows-universal-samples/blob/master/Samples/XamlMasterDetail/cs/MasterDetailPage.xaml
The xaml page:
<Page
x:Class="MyApp.Pages.ChatsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyApp.Pages"
xmlns:data="using:MyApp.Model.Profile"
xmlns:vm="using:MyApp.ViewModel"
xmlns:util="using:MyApp.Util"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Transitions>
<TransitionCollection>
<NavigationThemeTransition />
</TransitionCollection>
</Page.Transitions>
<Page.Resources>
<util:BoolToVisibilityConverter x:Key="BoolToVisConverter" />
<!--CollectionViewSource x:Name="Chats"
Source="{x:Bind ViewModel}"/>
<CollectionViewSource x:Name="Chat"
Source="{Binding ChatViewModel, Source={StaticResource Chats}}"/>
<CollectionViewSource x:Name="Messages"
Source="{Binding MessageViewModel, Source={StaticResource Chat}}"/-->
<DataTemplate x:Key="MasterListViewItemTemplate" >
<Grid Margin="0,11,0,13" BorderBrush="Gray" BorderThickness="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding ChatName}" Style="{ThemeResource ChatListTitleStyle}" />
<TextBlock
Text="{Binding LastMessage}"
Grid.Row="1"
MaxLines="1"
Style="{ThemeResource ChatListTextStyle}" />
<TextBlock
Text="{Binding LastSender}"
Grid.Column="1"
Margin="12,1,0,0"
Style="{ThemeResource ChatListLastSenderStyle}" />
</Grid>
</DataTemplate>
<DataTemplate x:Key="DetailContentTemplate">
<ListView x:Name="MessageList" ItemsSource="{Binding Messages}" ScrollViewer.VerticalScrollMode="Auto">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel BorderBrush="Black" BorderThickness="1" Padding="1">
<TextBlock Text="{Binding Message}" Style="{StaticResource NewsfeedTextStyle}"/>
<Image Visibility="{Binding Path=IsPhoto, Converter={StaticResource BoolToVisConverter} }" Source="{Binding Photo}" />
<Image Visibility="{Binding Path=IsReaction, Converter={StaticResource BoolToVisConverter} }" Width="200" Height="200" Source="{Binding Reaction}" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Sender}" Style="{StaticResource NewsfeedTimestampStyle}" Margin="1"/>
<TextBlock Text="{Binding SentTime}" Style="{StaticResource NewsfeedTimestampStyle}" Margin="1"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</Page.Resources>
<Grid x:Name="LayoutRoot" Loaded="LayoutRoot_Loaded">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="AdaptiveStates" CurrentStateChanged="AdaptiveStates_CurrentStateChanged">
<VisualState x:Name="DefaultState">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="720" />
</VisualState.StateTriggers>
</VisualState>
<VisualState x:Name="NarrowState">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="MasterColumn.Width" Value="*" />
<Setter Target="DetailColumn.Width" Value="0" />
<Setter Target="MasterListView.SelectionMode" Value="None" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="MasterColumn" Width="320" />
<ColumnDefinition x:Name="DetailColumn" Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Text="Chats"
Margin="12,8,8,8"
Style="{ThemeResource TitleTextBlockStyle}" />
<ListView
x:Name="MasterListView"
Grid.Row="1"
ItemContainerTransitions="{x:Null}"
ItemTemplate="{StaticResource MasterListViewItemTemplate}"
Background="{StaticResource ApplicationPageBackgroundThemeBrush}"
IsItemClickEnabled="True"
ItemClick="MasterListView_ItemClick">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
<ContentPresenter
x:Name="DetailContentPresenter"
Grid.Column="1"
Grid.RowSpan="2"
BorderThickness="1,0,0,0"
Padding="24,0"
BorderBrush="{ThemeResource SystemControlForegroundBaseLowBrush}"
Content="{x:Bind MasterListView.SelectedItem, Mode=OneWay}"
ContentTemplate="{StaticResource DetailContentTemplate}">
<ContentPresenter.ContentTransitions>
<!-- Empty by default. See MasterListView_ItemClick -->
<TransitionCollection />
</ContentPresenter.ContentTransitions>
</ContentPresenter>
</Grid>
I think it's the key point that your ListView is inside of the ContentTemplate of ContentPresenter.
Usually we can use ListViewBase.ScrollIntoView(Object) method method to scroll ListView to the specific item, but when the ListView is inside of DataTemplate, it is unexposed. Here is a method, we can use VisualTreeHelper to get this ListView:
public static T FindChildOfType<T>(DependencyObject root) where T : class
{
var queue = new Queue<DependencyObject>();
queue.Enqueue(root);
while (queue.Count > 0)
{
DependencyObject current = queue.Dequeue();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(current); i++)
{
var child = VisualTreeHelper.GetChild(current, i);
var typedChild = child as T;
if (typedChild != null)
{
return typedChild;
}
queue.Enqueue(child);
}
}
return null;
}
My sample is like this:
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="MasterColumn" Width="320" />
<ColumnDefinition x:Name="DetailColumn" Width="*" />
</Grid.ColumnDefinitions>
<ListView x:Name="MasterListView" Grid.Column="0" ItemsSource="{x:Bind ChatList}" SelectionChanged="MasterListView_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:ChatEntity">
<TextBlock Text="{x:Bind Member}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ContentPresenter x:Name="DetailContentPresenter" Grid.Column="1"
Content="{x:Bind MasterListView.SelectedItem, Mode=OneWay}">
<ContentPresenter.ContentTemplate>
<DataTemplate x:DataType="local:ChatEntity">
<Grid>
<Grid.Resources>
<DataTemplate x:Key="FromMessageDataTemplate">
<StackPanel Orientation="Horizontal" FlowDirection="LeftToRight">
<TextBlock Text="{Binding Member}" Width="30" Foreground="Blue" FontWeight="Bold" />
<TextBlock Text=":" Width="10" Foreground="Blue" FontWeight="Bold" />
<TextBlock Text="{Binding Content}" Foreground="Red" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="ToMessageDataTemplate">
<StackPanel Orientation="Horizontal" FlowDirection="RightToLeft">
<TextBlock Text="Me" Width="30" HorizontalAlignment="Right" Foreground="Blue" FontWeight="Bold" />
<TextBlock Text=":" Width="10" HorizontalAlignment="Right" Foreground="Blue" FontWeight="Bold" />
<TextBlock Text="{Binding Content}" HorizontalAlignment="Right" Foreground="Green" />
</StackPanel>
</DataTemplate>
<local:ChatDataTemplateSelector x:Key="ChatDataTemplateSelector"
MessageFromTemplate="{StaticResource FromMessageDataTemplate}"
MessageToTemplate="{StaticResource ToMessageDataTemplate}" />
</Grid.Resources>
<ListView ItemsSource="{x:Bind MessageList}" ItemTemplateSelector="{StaticResource ChatDataTemplateSelector}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
</Grid>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
The ChatEntity class and MessageEntity class are like this:
public class ChatEntity
{
public string Member { get; set; }
public ObservableCollection<MessageEntity> MessageList { get; set; }
}
public class MessageEntity
{
public enum MsgType
{
From,
To
}
public string Member { get; set; }
public string Content { get; set; }
public MsgType MessageType { get; set; }
}
and my ChatDataTemplateSelector is like this:
public class ChatDataTemplateSelector : DataTemplateSelector
{
public DataTemplate MessageFromTemplate { get; set; }
public DataTemplate MessageToTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
MessageEntity msg = item as MessageEntity;
if (msg != null)
{
if (msg.MessageType == MessageEntity.MsgType.From)
return MessageFromTemplate;
else
return MessageToTemplate;
}
return null;
}
}
Firstly I loaded the ChatList in the left ListView, and in the SelectionChanged event of the left ListView, I loaded the MessageList, this will ensure the MessageList be updated. It's like manually refreshing ObservableCollection(MessageList) for the right ListView each time you selected a item in the left ListView. But you can also add data to the MessageList in other time, and add data to it whenever there is a new message. The ObservableCollection can automatically get refresh. Here is my code:
private ObservableCollection<MessageEntity> messageList;
private ObservableCollection<ChatEntity> ChatList;
public MainPage()
{
this.InitializeComponent();
messageList = new ObservableCollection<MessageEntity>();
ChatList = new ObservableCollection<ChatEntity>();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
ChatList.Add(new ChatEntity { Member = "Tom", MessageList = messageList });
ChatList.Add(new ChatEntity { Member = "Peter" });
ChatList.Add(new ChatEntity { Member = "Clark" });
}
private void MasterListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
messageList.Clear();
for (int i = 0; i < 100; i++)
{
if (i % 2 == 0)
messageList.Add(new MessageEntity { Member = "Tom", Content = "Hello!", MessageType = MessageEntity.MsgType.From });
else
messageList.Add(new MessageEntity { Content = "World!", MessageType = MessageEntity.MsgType.To });
}
var listView = FindChildOfType<ListView>(DetailContentPresenter);
listView.ScrollIntoView(messageList.Last());
}
Data in my sample are all fake. The sample looks a little complex, but it's actually very simple, just use VisualTreeHelper to find the ListView and use its ScrollIntoView method to scroll to the last item.

Xaml WinRT Custom User Control with Bindable Collection

I am trying to make a custom user control that I can reuse across all of my views. My BaseViewModel has a property called ViewAlerts that is intended to be used to show alerts consistently across the application (such as successful updates, failed requests, etc.). I was able to get to the point where my custom control is buildable and has a property for binding, but the collection of alerts is never show. I am statically defining some alerts in my base view model for testing purposes, and am not able to get the alerts to show (seems like a problem with an INotifyPropertyChanged but my bindable property inherits from ObservableCollection<> so it should be automatically handling that I think).
Here is my custom control so far:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Resources/MMJCeoResources.xaml" />
<ResourceDictionary>
<Style TargetType="TextBlock" x:Key="AlertTextStyle">
<Setter Property="FontSize" Value="15"></Setter>
<Setter Property="Margin" Value="10" />
<Setter Property="Padding" Value="10" />
</Style>
<DataTemplate x:Key="DangerAlert">
<Grid Background="LightPink">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="10*"/>
</Grid.RowDefinitions>
<Border Width="10"
Height="10"
BorderBrush="DarkRed"
HorizontalAlignment="Right"
Margin="5"
Grid.Row="0">
<TextBlock Text="X" />
</Border>
<TextBlock Grid.Row="1" Text="{Binding Message}" Style="{StaticResource AlertTextStyle}" Foreground="DarkRed"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="SuccessAlert">
<Grid Background="LightGreen">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="10*"/>
</Grid.RowDefinitions>
<Border Width="10"
Height="10"
BorderBrush="DarkGreen"
HorizontalAlignment="Right"
Margin="5"
Grid.Row="0">
<TextBlock Text="X" />
</Border>
<TextBlock Grid.Row="1" Text="{Binding Message}" Style="{StaticResource AlertTextStyle}" Foreground="DarkGreen"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="InfoAlert">
<Grid Background="LightGreen">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="10*"/>
</Grid.RowDefinitions>
<Border Width="10"
Height="10"
BorderBrush="DarkGreen"
HorizontalAlignment="Right"
Margin="5"
Grid.Row="0">
<TextBlock Text="X" />
</Border>
<TextBlock Grid.Row="1" Text="{Binding Message}" Style="{StaticResource AlertTextStyle}" Foreground="DarkGreen"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="WarningAlert">
<Grid Background="LightSalmon">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="10*"/>
</Grid.RowDefinitions>
<Border Width="10"
Height="10"
BorderBrush="DarkOrange"
HorizontalAlignment="Right"
Margin="5"
Grid.Row="0">
<TextBlock Text="X" />
</Border>
<TextBlock Grid.Row="1" Text="{Binding Message}" Style="{StaticResource AlertTextStyle}" Foreground="DarkOrange"/>
</Grid>
</DataTemplate>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding Path=Alerts}"
ItemTemplateSelector="{StaticResource AlertDataTemplateSelector}">
</ItemsControl>
</Grid>
</UserControl>
The Code Behind for the control:
public sealed partial class AlertControl : UserControl
{
public static readonly DependencyProperty AlertsProperty = DependencyProperty.Register(
"Alerts", typeof (ObservableList<Alert>), typeof (AlertControl), new PropertyMetadata(default(ObservableList<Alert>), OnAlertsChanged));
public ObservableList<Alert> Alerts
{
get { return (ObservableList<Alert>) GetValue(AlertsProperty); }
set { SetValue(AlertsProperty, value); }
}
//I was trying to adapt another tutorial to what I was trying to accomplish but only got this far
public static void OnAlertsChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var old = e.OldValue as ObservableList<Alert>;
var me = sender as AlertControl;
if (old != null)
{
old.CollectionChanged -= me.OnAlertCollectionChanged;
}
var n = e.NewValue as ObservableList<Alert>;
if (n != null)
n.CollectionChanged += me.OnAlertCollectionChanged;
}
private void OnAlertCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Reset)
{
Alerts.Clear();
var n = e.NewItems as ObservableList<Alert>;
Alerts.AddRange(n);
}
}
public AlertControl()
{
this.InitializeComponent();
DataContext = this;
}
}
an example implementation:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Vertical" HorizontalAlignment="Stretch">
<TextBlock Style="{StaticResource PageTitle}" Text="User Information" />
<controls:AlertControl Alerts="{Binding ViewAlerts}" />
in this implementation, the ViewAlerts property has 4 statically defined alerts in it, so I know there are values that should be showing up.
You should give your inner Grid the DataContext to this and not to the Control itself, because then the outer binding will search for your ViewAlerts inside the Control
<Grid x:Name="InnerGrid">
<ItemsControl ItemsSource="{Binding Path=Alerts}"
ItemTemplateSelector="{StaticResource AlertDataTemplateSelector}">
</ItemsControl>
</Grid>
public AlertControl()
{
this.InitializeComponent();
InnerGrid.DataContext = this;
}
After that you can Bind to Alerts, and Alerts will be binded to the ItemsControl inside your InnerGrid

How to Attach Command to Event in WPF Using MVVM - DataGrid Selection Changed to Update the RowCount Displayed in a TextBlock

In order to attempt to get around the problem I outline in Binding Selected RowCount to TextBlock not Firing OnPropertyChanged after DataGrid Scroll; namely updating a TextBlock with the currently selected row count of a DataGrid when it scrolls. I have attempted to introduce the AttachedCommandBehaviour designed by Marlon Grech [an amazing class structure that allows you to bind commands to specific events of a given control].
Now for the question, using this AttachedCommandBehaviour, how can I get a TextBlock to update based upon a DataGrid's SelectionChangedProperty?
The XMAL is
<Window x:Class="ResourceStudio.Views.AddCultureWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModels="clr-namespace:ResourceStudio.ViewModels"
xmlns:converters="clr-namespace:ResourceStudio.Converters"
xmlns:dataAccess="clr-namespace:ResourceStudio.DataAccess"
xmlns:attachedCommand="clr-namespace:AttachedCommandBehavior;assembly=AttachedCommandBehavior"
Title="Add Culture"
Height="510" Width="400"
WindowStartupLocation="CenterOwner"
VerticalContentAlignment="Stretch"
MinWidth="380" MinHeight="295">
<DockPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="24"/>
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="15*"/>
<ColumnDefinition Width="377*"/>
<ColumnDefinition Width="15*"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="1">
<Grid>
<Grid.Resources>
<converters:EnumToBooleanConverter x:Key="enumToBooleanConverter"/>
<Style x:Key="HyperlinkButton" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<GroupBox Header="Filters" Grid.Row="0" Margin="0,0,0,5">
<StackPanel VerticalAlignment="Top">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="2,2,2,2">
<RadioButton Content="All Cultures" Margin="10,5,10,5"
HorizontalAlignment="Left"
IsChecked="{Binding SelectedFilterType,
Converter={StaticResource enumToBooleanConverter},
ConverterParameter=AllCultures}"/>
<RadioButton Content="Neutral Cultures" Margin="10,5,10,5"
HorizontalAlignment="Left"
IsChecked="{Binding SelectedFilterType,
Converter={StaticResource enumToBooleanConverter},
ConverterParameter=NeutralCultures}"/>
<RadioButton Content="Specific Cultures" Margin="10,5,10,5"
HorizontalAlignment="Left"
IsChecked="{Binding SelectedFilterType,
Converter={StaticResource enumToBooleanConverter},
ConverterParameter=SpecificCultures}"/>
</StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="Language:" Grid.Column="0"/>
<TextBox HorizontalAlignment="Stretch" Grid.Column="1"
Margin="2,0,2,0" Height="22"/>
</Grid>
</StackPanel>
</GroupBox>
<DataGrid x:Name="cultureDataGrid" Grid.Row="1" AlternatingRowBackground="Gainsboro" AlternationCount="2"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
AutoGenerateColumns="False" RowHeaderWidth="0" IsReadOnly="True"
CanUserAddRows="False" CanUserDeleteRows="False" SelectionMode="Extended"
EnableRowVirtualization="True" EnableColumnVirtualization="True"
ItemsSource="{Binding Cultures, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged, IsAsync=True}">
<DataGrid.Columns>
<DataGridTextColumn Header="Code" Binding="{Binding Code}" IsReadOnly="True"/>
<DataGridTextColumn Header="Language" Binding="{Binding Language}" IsReadOnly="True"/>
<DataGridTextColumn Header="LocalName" Binding="{Binding LocalName}" IsReadOnly="True"/>
</DataGrid.Columns>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#FF007ACC"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay, IsAsync=True}" />
</Style>
</DataGrid.RowStyle>
</DataGrid>
<StackPanel Grid.Row="2" HorizontalAlignment="Right">
<Button Name="button1" Style="{StaticResource HyperlinkButton}"
Focusable="False">
<TextBlock>
<Hyperlink Focusable="False">
Select All
</Hyperlink>
</TextBlock>
</Button>
</StackPanel>
<GroupBox Grid.Row="3" Header="Options">
<CheckBox Content="Copy default values" Margin="3,3"/>
</GroupBox>
<StackPanel Grid.Row="4" Orientation="Horizontal"
HorizontalAlignment="Right" Margin="0,2,0,2">
<Button Content="Select" Width="70" Height="25"
Margin="0,5,5,5" HorizontalAlignment="Right"
VerticalContentAlignment="Center" IsDefault="True"/>
<Button Content="Cancel" Width="70" Height="25"
Margin="5,5,0,5" HorizontalAlignment="Right"
VerticalContentAlignment="Center" IsCancel="True"/>
</StackPanel>
</Grid>
</Grid>
</Grid>
<StatusBar Grid.Row="1" Margin="0,0.4,0.4,-0.4">
<StatusBarItem DockPanel.Dock="Left" Background="#FF007ACC" Margin="0,2,0,0">
<TextBlock Text="{Binding TotalSelectedCultures}" Margin="5,0,0,0" Foreground="White"/>
</StatusBarItem>
</StatusBar>
</Grid>
</DockPanel>
</Window>
My ViewModel is
public class CultureDataViewModel : ViewModelBase
{
public FilterType SelectedFilterType { get; private set; }
public ICollectionView CulturesView { get; private set; }
public MultiSelectCollectionView<CultureViewModel> Cultures { get; private set; }
public CultureDataViewModel()
{
SelectedFilterType = FilterType.AllCultures;
LoadCultures();
}
void OnCultureViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
string IsSelected = "IsSelected";
(sender as CultureViewModel).VerifyPropertyName(IsSelected);
if (e.PropertyName == IsSelected)
this.OnPropertyChanged("TotalSelectedCultures");
}
void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null && e.NewItems.Count != 0)
foreach (CultureViewModel cultVm in e.NewItems)
cultVm.PropertyChanged += this.OnCultureViewModelPropertyChanged;
if (e.OldItems != null && e.OldItems.Count != 0)
foreach (CultureViewModel cultVm in e.OldItems)
cultVm.PropertyChanged -= this.OnCultureViewModelPropertyChanged;
}
public void LoadCultures()
{
// Fill the Culutres collection...
}
public string TotalSelectedCultures
{
get
{
int selectedCultures = this.Cultures.SelectedItems.Count;
return String.Format("{0:n0} of {1:n0} cultures selected",
selectedCultures,
Cultures.Count);
}
}
}
The link to a downloadable example of the use of the AttachedCommandBehaviour is Here. Your help is most appreciated...
So, as far as I understand, you want to have TextBlock which says:
"5 of 100 items selected"
I've reconstructed your window, the button below the list shows how many items you have selected.
Is that correct? If that is really all you want to do, you don't need to involve the ViewModel at all:
All I did, was this:
<Button Content="{Binding SelectedItems.Count, ElementName=cultureDataGrid}" />
Does this help in some way already or is there a reason for using the pretty complex method with event to command etc.?
EDIT
Your problem basically melts down to "How can I bind to SelectedItems property of a DataGrid. Here is how I solved it:
Define a behavior with a dependency property for the DataGrid, which relays the SelectedItems of the DataGrid to the DependencyProperty:
public class BindableSelectedItems : Behavior<DataGrid>
{
public static readonly DependencyProperty SelectedItemsProperty =
DependencyProperty.Register("SelectedItems", typeof (IList), typeof (BindableSelectedItems), new PropertyMetadata(default(IList), OnSelectedItemsChanged));
private static void OnSelectedItemsChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
var grid = ((BindableSelectedItems) sender).AssociatedObject;
if (grid == null) return;
// Add logic to select items in grid
}
public IList SelectedItems
{
get { return (IList) GetValue(SelectedItemsProperty); }
set { SetValue(SelectedItemsProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.SelectionChanged += AssociatedObject_SelectionChanged;
}
void AssociatedObject_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var grid = (DataGrid) sender;
SelectedItems = grid.SelectedItems;
}
}
Attach this behavior to the DataGrid and bind the SelectedItems property to a property on your ViewModel:
<DataGrid x:Name="cultureDataGrid" ItemsSource="{Binding Cultures}">
<i:Interaction.Behaviors>
<behaviors:BindableSelectedItems x:Name="CulturesSelection"
SelectedItems="{Binding SelectedCultures, Mode=OneWayToSource}"/>
</i:Interaction.Behaviors>
with the property on your ViewModel like this:
public IList SelectedCultures
{
get { return _selectedCultures; }
set
{
_selectedCultures = value;
OnPropertyChanged("SelectedCultures");
}
}
If you only want to get the count of selected items, you can bind to the behavior directly and don't need a field on the ViewModel for this:
<TextBlock Text="{Binding Path=SelectedItems.Count, ElementName=CulturesSelection}" Margin="5,0,0,0" Foreground="White"/>
In your ViewModel you can use the SelectedItems property to work with the selection:
var selectedCultures= SelectedCultures.OfType<CultureViewModel>();
I've uploaded the solution, see the link in the comments.
I hope this helps and whish you good luck! There always are a thousand ways to do stuff in WPF and it is really hard to find the best one sometimes...

Categories

Resources