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 ***
}
Related
When i try to set VirtualizationMode on my ListView to Recycling I get the error from the title:
Cannot change the VirtualizationMode attached property on an
ItemsControl after Measure is called on the ItemsHost panel.
I am trying to set the attached property programmatically but when I try to define the VirtualizationMode in XAML designer throws the same error from the title. Anyone had similar problems like this?
My view in XAML is:
<Window x:Class="FinalVirtualizationApp.MainWindow"
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:local="clr-namespace:FinalVirtualizationApp"
mc:Ignorable="d"
Title="MainWindow" Height="900" Width="800"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
TextElement.Foreground="{DynamicResource MaterialDesignBody}"
TextElement.FontWeight="Regular"
TextElement.FontSize="13"
TextOptions.TextFormattingMode="Ideal"
TextOptions.TextRenderingMode="Auto"
Background="{DynamicResource MaterialDesignPaper}"
FontFamily="{DynamicResource MaterialDesignFont}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<GroupBox Grid.Row="0" Header="UI virtualization options">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<CheckBox Content="UI virtualization" VerticalAlignment="Center" IsChecked="{Binding IsUIVirtualization}"/>
<Grid Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="Container Recycling:" VerticalAlignment="Center"/>
<ComboBox Grid.Column="1" VerticalAlignment="Center" SelectedValue="{Binding ContainerRecyclingType}" SelectedValuePath="Content">
<ComboBoxItem>Recycling</ComboBoxItem>
<ComboBoxItem>Standard</ComboBoxItem>
</ComboBox>
</Grid>
<CheckBox Content="Deferred scrolling" Grid.Row="1" VerticalAlignment="Center" IsChecked="{Binding IsDeferredScrolling}">
</CheckBox>
<Grid Grid.Row="1" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="74*"/>
<ColumnDefinition Width="253*"/>
</Grid.ColumnDefinitions>
<Label Content="Scroll unit" VerticalAlignment="Center" Margin="0,0,0,1"/>
<ComboBox Grid.Column="1" VerticalAlignment="Center" SelectedValue="{Binding ScrollUnitType}" SelectedValuePath="Content" Grid.ColumnSpan="2" Margin="0,2,0,3">
<ComboBoxItem>Item</ComboBoxItem>
<ComboBoxItem>Pixel</ComboBoxItem>
</ComboBox>
</Grid>
</Grid>
</GroupBox>
<GroupBox Grid.Row="1" Header="Data virtualization">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<GroupBox Header="ItemsProvider">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<Label Content="Number of Items: "/>
<TextBox Width="60" Text="{Binding NumberOfItems}"/>
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Horizontal">
<Label Content="Fetch delay(ms): "/>
<TextBox Width="60" Text="{Binding FetchDelay}"/>
</StackPanel>
</Grid>
</GroupBox>
<GroupBox Grid.Row="1" Header="Collection">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,2,0,0">
<TextBlock Text="Type:" Margin="5" TextAlignment="Right" VerticalAlignment="Center"/>
<RadioButton x:Name="rbNormal" GroupName="rbGroup" Margin="5" Content="List(T)" VerticalAlignment="Center" Command="{Binding CollectionTypeChangeCommand}" CommandParameter="List"/>
<RadioButton x:Name="rbVirtualizing" GroupName="rbGroup" Margin="5" Content="VirtualizingList(T)" VerticalAlignment="Center" Command="{Binding CollectionTypeChangeCommand}" CommandParameter="VirtualizingList"/>
<RadioButton x:Name="rbAsync" GroupName="rbGroup" Margin="5" Content="AsyncVirtualizingList(T)" VerticalAlignment="Center" Command="{Binding CollectionTypeChangeCommand}" CommandParameter="AsyncVirtualizingList"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,2,0,0">
<TextBlock Text="Page size:" Margin="5" TextAlignment="Right" VerticalAlignment="Center"/>
<TextBox x:Name="tbPageSize" Margin="5" Text="{Binding PageSize}" Width="60" VerticalAlignment="Center"/>
<TextBlock Text="Page timeout (s):" Margin="5" TextAlignment="Right" VerticalAlignment="Center"/>
<TextBox x:Name="tbPageTimeout" Margin="5" Text="{Binding PageTimeout}" Width="60" VerticalAlignment="Center"/>
</StackPanel>
</StackPanel>
</GroupBox>
</Grid>
</GroupBox>
<StackPanel Orientation="Horizontal" Grid.Row="2">
<TextBlock Text="Memory Usage:" Margin="5" VerticalAlignment="Center"/>
<TextBlock x:Name="tbMemory" Margin="5" Width="80" Text="{Binding MemoryUsage}" VerticalAlignment="Center"/>
<Button Content="Refresh" Margin="5" Width="100" VerticalAlignment="Center" Command="{Binding RefreshCommand}"/>
</StackPanel>
<ListView Grid.Row="3" Name="lvItems">
<ListView.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding DeviceName}">
<StackPanel>
</StackPanel>
</Expander>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Window>
And i try to set the VirtualizationMode here:
public void SetUIVirtualizationOptions()
{
listView.SetValue(VirtualizingStackPanel.IsVirtualizingProperty, IsUIVirtualization);
listView.SetValue(ScrollViewer.IsDeferredScrollingEnabledProperty, IsDeferredScrolling);
if(ContainerRecyclingType == "Recycling")
listView.SetValue(VirtualizingStackPanel.VirtualizationModeProperty, VirtualizationMode.Recycling);
else
listView.SetValue(VirtualizingStackPanel.VirtualizationModeProperty, VirtualizationMode.Standard);
if (ScrollUnitType == "Item")
listView.SetValue(VirtualizingPanel.ScrollUnitProperty, ScrollUnit.Item);
else
listView.SetValue(VirtualizingPanel.ScrollUnitProperty, ScrollUnit.Pixel);
}
Edit: My Window code behind where i pass the listview to viewmodel is:
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel(lvItems);
}
}
And the viewmodel part of the code (minus getters and setters for less space taken):
public class MainWindowViewModel : BindableBase
{
#region private fields
private ListView listView;
private bool isUIVirtualization;
private bool isDeferredScrolling;
private string containerRecyclingType;
private string scrollUnitType;
private int numberOfItems;
private int fetchDelay;
private string collectionType;
private int pageSize;
private int pageTimeout;
private string memoryUsage;
private DemoItemProvider itemsProvider;
#endregion
#region commands
public RelayCommand<string> CollectionTypeChangeCommand { get; set; }
public RelayCommand RefreshCommand { get; set; }
public RelayCommand<string> ContainerRecyclingTypeChangeCommand { get; set; }
public RelayCommand<string> ScrollUnitTypeChangeCommand { get; set; }
#endregion
public MainWindowViewModel(ListView lvItems)
{
this.listView = lvItems;
PageSize = 100;
PageTimeout = 30;
NumberOfItems = 1000000;
FetchDelay = 1000;
CollectionTypeChangeCommand = new RelayCommand<string>(CollectionTypeChangeFunc);
RefreshCommand = new RelayCommand(RefreshFunc);
ContainerRecyclingTypeChangeCommand = new RelayCommand<string>(ContainerRecyclingTypeChangeFunc);
ScrollUnitTypeChangeCommand = new RelayCommand<string>(ScrollUnitTypeChangeFunc);
// use a timer to periodically update the memory usage
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 1);
timer.Tick += timer_Tick;
timer.Start();
}
private void timer_Tick(object sender, EventArgs e)
{
MemoryUsage = string.Format("{0:0.00} MB", GC.GetTotalMemory(true) / 1024.0 / 1024.0);
}
#region command methods
public void CollectionTypeChangeFunc(string type)
{
CollectionType = type;
}
public void RefreshFunc()
{
SetUIVirtualizationOptions();
itemsProvider = new DemoItemProvider(NumberOfItems, FetchDelay);
if (collectionType == "List")
{
listView.ItemsSource = new List<DataItem>(itemsProvider.FetchRange(0, itemsProvider.FetchCount()));
}
else if (collectionType == "VirtualizingList")
{
listView.ItemsSource = new VirtualizingCollection<DataItem>(itemsProvider, pageSize);
}
else if (collectionType == "AsyncVirtualizingList")
{
listView.ItemsSource = new AsyncVirtualizingCollection<DataItem>(itemsProvider, pageSize, pageTimeout * 1000);
}
}
public void ContainerRecyclingTypeChangeFunc(string type)
{
ContainerRecyclingType = type;
}
public void ScrollUnitTypeChangeFunc(string type)
{
ScrollUnitType = type;
}
public void SetUIVirtualizationOptions()
{
listView.SetValue(VirtualizingStackPanel.IsVirtualizingProperty, IsUIVirtualization);
listView.SetValue(ScrollViewer.IsDeferredScrollingEnabledProperty, IsDeferredScrolling);
if(ContainerRecyclingType == "Recycling")
listView.SetValue(VirtualizingStackPanel.VirtualizationModeProperty, VirtualizationMode.Recycling);
else
listView.SetValue(VirtualizingStackPanel.VirtualizationModeProperty, VirtualizationMode.Standard);
if (ScrollUnitType == "Item")
listView.SetValue(VirtualizingPanel.ScrollUnitProperty, ScrollUnit.Item);
else
listView.SetValue(VirtualizingPanel.ScrollUnitProperty, ScrollUnit.Pixel);
}
#endregion
}
The code source for VirtualizingStackPanel indicates that this property can only be set before initialization of the panel:
/// <summary>
/// Attached property for use on the ItemsControl that is the host for the items being
/// presented by this panel. Use this property to modify the virtualization mode.
///
/// Note that this property can only be set before the panel has been initialized
/// </summary>
public static readonly DependencyProperty VirtualizationModeProperty =
DependencyProperty.RegisterAttached("VirtualizationMode", typeof(VirtualizationMode), typeof(VirtualizingStackPanel),
new FrameworkPropertyMetadata(VirtualizationMode.Standard));
You can indeed check this behavior later in the file:
//
// Set up info on first measure
//
if (HasMeasured)
{
VirtualizationMode oldVirtualizationMode = InRecyclingMode ? VirtualizationMode.Recycling : VirtualizationMode.Standard;
if (oldVirtualizationMode != virtualizationMode)
{
throw new InvalidOperationException(SR.Get(SRID.CantSwitchVirtualizationModePostMeasure));
}
}
else
{
HasMeasured = true;
}
and there is no way (according to source code) to set this HasMeasured property back to False unless you destroy and recreate the ListView.
It is so as the message says:
Your not allowed to _change the VirtualizationMode attached property
on an ItemsControl after Measure is called on the ItemsHost panel.
This means, that if ListView is already shown virtualizing mechanism being used and you are not allowed to change it.
If you set Visibility of ListView in XAML to Collapsed and set it only later to the Visible, then you can set the VirtualizationMode in code behind to wished value, but only once(so you can't change it after ListView become visible)!
private void Button_Click(object sender, RoutedEventArgs e)
{
listView.SetValue(VirtualizingStackPanel.VirtualizationModeProperty, VirtualizationMode.Recycling);
listView.Visibility = Visibility.Visible;
}
XAML:
<ListView x:Name="listView" ... Visibility="Collapsed">
I am frustrated with the listview ItemSource and MVVM binding. I am binding the listview with a data model. After loading the listview, I add another item to the listview, the update is not reflected in listview, but it shows that the number of items in the list collection is increased by one. If I add the add in the list collection in the constructor, it is shown in the listview.
XAML
<Page
x:Class="App20.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App20"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.DataContext>
<local:VictimsController></local:VictimsController>
</Page.DataContext>
<Grid Padding="10">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<TextBlock Text="List of Victims/Trapped" HorizontalAlignment="Center" Style="{StaticResource HeaderTextBlockStyle}"/>
</Grid>
<Grid Grid.Row="1">
<ListView x:Name="ls" ItemsSource="{Binding VictimList, Mode=TwoWay}">
<ListView.HeaderTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock FontWeight="Bold" HorizontalAlignment="Center" Grid.Column="0" Text="GPSLatitute" />
<TextBlock FontWeight="Bold" HorizontalAlignment="Center" Grid.Column="1" Text="GPSLongtitude" />
<TextBlock FontWeight="Bold" HorizontalAlignment="Center" Grid.Column="2" Text="Date" />
</Grid>
</DataTemplate>
</ListView.HeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock HorizontalAlignment="Center" Grid.Column="0" Text="{Binding GPSLatitute}" />
<TextBlock HorizontalAlignment="Center" Grid.Column="1" Text="{Binding GPSLongtitude}" />
<TextBlock HorizontalAlignment="Center" Grid.Column="2" Text="{Binding Date}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Grid>
<Button Content="Publish" VerticalAlignment="Top"
Height="76" Width="114" Click="Publish_Click" />
</Grid>
Controller
public class VictimsController : INotifyPropertyChanged
{
List<VictimProfile> _victims = new List<VictimProfile>();
public VictimsController()
{
//Victims.Add(new VictimProfile() { GPSLatitute = 123, GPSLongtitude = 2333 });
}
public List<VictimProfile> VictimList
{
get
{
return _victims;
}
set
{
_victims = value;
OnPropertyChanged("VictimList");
}
}
public void addVictim(VictimProfile profile)
{
_victims.Add(profile);
OnPropertyChanged("VictimList");
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Adding one item to the listview model or collection
async void client_MqttMsgPublishReceived(object sender, MqttMsgPublishEventArgs e)
{
string ReceivedMessage = Encoding.UTF8.GetString(e.Message);
//convert the message to json format
var payLoad = JsonConvert.DeserializeObject<VictimProfile>(ReceivedMessage);
// we need this construction because the receiving code in the library and the UI with textbox run on different threads
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {
txt.Text += ReceivedMessage;
VictimsController model = this.DataContext as VictimsController;
model.addVictim(payLoad); //add item to the list collection
Debug.WriteLine("count: " + ls.Items.Count); //this shows that listview has one more item added to it, but nothing is shown in the listview
//ls.Items.Add(payLoad);
});
}
The point is the list collection has the new item and the ls (listview) also has the item, but it is not shown in the listview.
Finally I solved it.
Changing the list to observable collection has solved the problem.
ObservableCollection<VictimProfile> _victims = new ObservableCollection<VictimProfile>();
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
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.
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