I want to display multiple rectangles with some properties, which I store in an Array of Arrays. The XAML for displaying looks like this:
<UserControl x:Class="Project.Views.SomeView">
<UserControl.Resources>
<DataTemplate x:Key="ObjectRowTemplate">
<Rectangle Fill="{Binding Path=color, UpdateSourceTrigger=PropertyChanged}" Height="15" Width="30" StrokeThickness="1px" Stroke="Blue" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown">
<catel:EventToCommand DisableAssociatedObjectOnCannotExecute="False">
<catel:EventToCommand.Command>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type Canvas}}" Path="DataContext.ObjectClickedCommand" />
</catel:EventToCommand.Command>
<catel:EventToCommand.CommandParameter>
<Binding Path="." />
</catel:EventToCommand.CommandParameter>
</catel:EventToCommand>
</i:EventTrigger>
</i:Interaction.Triggers>
</Rectangle>
</DataTemplate>
<DataTemplate x:Key="ObjectColumnTemplate">
<ItemsControl ItemsSource="{Binding ., UpdateSourceTrigger=PropertyChanged}" ItemTemplate="{DynamicResource ObjectRowTemplate}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
</UserControl.Resources>
<StackPanel>
<Canvas Background="Black" Width="{Binding CanvasWidth}" Height="600">
<Grid>
<ItemsControl ItemsSource="{Binding Path=BindedProperty, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" ItemTemplate="{DynamicResource ObjectColumnTemplate}"/>
</Grid>
</Canvas>
</StackPanel>
there is a StackPanel
with a canvas inside which shows the array of array via the two defined DataTemplate
s, by creating a Rectangle
for each "slot" in the Array
.
namespace ViewModels
{
public class ViewModel : AdvancedViewModelBase
{
public CreateGameFieldViewModel()
{
ObjectClickedCommand = new Command<Object>(ObjectClicked);
Objects = new Objects[15][];
for (int i = 0; i < 15; i++)
{
Objects[i] = new Object[15];
for (int j = 0; j < 15; j++)
{
Objects[i][j] = new Object(someparams);
}
}
}
private Object _selectedObject;
private Object[][] _object;
public Object[][] Objects
{
get { return _object; }
set
{
_object = value;
RaisePropertyChanged(nameof(Objects));
}
}
public int CanvasWidth { get; set; } = 450;
public Command<OPbject> ObjectClickedCommand { get; set; }
public void ObjectClicked(Object object)
{
if (object != null)
{
_selectedObject = object;
}
}
public void OnKeyDown(KeyEventArgs e)
{
// Modify _currentObject
_currentObject.color = Brushes.Blue;
RaisePropertyChanged(nameof(Objects));
}
}
}
The property binding with the viewmodel works fine, the properties are set as defined. By clicking on a Rectangle
, the object which is bound to, is now set as _selectedObject
and can be modified by the arrow keys, which also works like a charm.
Since I modified one object of the array of arrays, which I am binding to the DataTemplate, I have to notify my UI which I am doing by then line RaisePropertyChanged(nameof(Objects));
(used Nuget: Catel) after the modification.
Unfortunately the UI does not get updated. I checked everything, the object in the array of arrays contains the modifications.
I guess it doesn't work because of the DataTemplate
s I used.
Can anyone help me?
I am facing the same issue in UWP when the ObservableCollection
of the second level of the parent ObservableCollection
won't update UI in DataTemplate
though it has implemented INotifyPropertyChanged
.
It seems like Routing
does not work for the nested ObservableCollection
.
I also was not able to force the method myObject.OnPropertyChanged('PropertyName')
.
So the solution was folllowing:
Dynamically populate Tag
property of the DataTemplate
using ObjectToString
converter. So the Tag
has a string like Author-3 . Where is 3 is the ID of the Author entity and as result we have unique identifiers for all objects created on base of DataTemplate
.
Within code just find all objects via Object Type
and the Tag
name (ie Author-3) and do what you need to change its any property.
And you have working UI!
In my case I will provide chunks of code and you will have whole imagination what's going on.
XAML
xmlns:myconverters="using:MyProjectFolder.Converters"
<UserControl.Resources>
<myconverters:LoaderOnlineToColorConverter x:Key="MyLoaderOnlineToColorConverter"/>
<muxc:StackLayout x:Key="UniformGridLayout2" />
<DataTemplate x:Key="UnitLoaderItemTemplate" x:DataType="x1:UnitLoader">
<Grid BorderBrush="Gray" BorderThickness="1" Margin="0,5,0,0" Padding="5,0,5,5" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="6*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel IsDoubleTapEnabled="True" DoubleTapped="Unit_DoubleTapped" Tag="{x:Bind DeviceID}"
Grid.Column="0" Padding="2,0,2,4" Margin="0,5,0,0" Orientation="Horizontal"
BorderBrush="Gray" BorderThickness="1" ToolTipService.ToolTip="{x:Bind FullName}">
<FontIcon Tag="{x:Bind Converter={StaticResource MyDeviceIDToControlNameConverter}}"
Foreground="{x:Bind Converter={StaticResource MyLoaderOnlineToColorConverter}}"
FontFamily="Segoe MDL2 Assets" Glyph="" VerticalAlignment="Center" Margin="0,0,2,0" />
<TextBlock Text="{x:Bind DeviceName}" VerticalAlignment="Center" />
</StackPanel>
NOTE: this line Foreground="{x:Bind Converter={StaticResource MyLoaderOnlineToColorConverter}}"
exactly does not work so we have another one
Tag="{x:Bind Converter={StaticResource MyDeviceIDToControlNameConverter}}"
C# The converters
public class DeviceIDToControlNameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var controlName = "Unit-";
if (value is UnitLoader)
{
controlName = "Loader-" + ((UnitLoader)value).DeviceID;
}
else if (value is UnitLoader)
{
controlName = "Track-" + ((UnitTrack)value).DeviceID;
}
return controlName;
}
}
public class LoaderOnlineToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
SolidColorBrush color;
if (value is UnitLoader)
{
var isOnline = ((UnitLoader)value).IsOnline;
if (isOnline)
{
color = new SolidColorBrush(Colors.YellowGreen);
}
else
{
color = new SolidColorBrush(Colors.DarkGray);
}
}
else
{
color = new SolidColorBrush(Colors.DarkGray);
}
return color;
}
}
C# The code to change the property of all objects
string tagName = "Loader-" + unitLoader.DeviceID.ToString();
var fontIcon = FindControl<FontIcon>(this, typeof(FontIcon), tagName);
if (fontIcon != null)
{
fontIcon.Foreground = unitLoader.IsOnline ? deviceOnline : deviceOffline;
}
C# Method to search all objects via Type and Tag
public static T FindControl<T>(UIElement parent, Type targetType, string ControlName) where T : FrameworkElement
{
if (parent == null) return null;
if (parent.GetType() == targetType && ((T)parent).Tag != null && ((T)parent).Tag.ToString() == ControlName)
{
return (T)parent;
}
T result = null;
int count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++)
{
UIElement child = (UIElement)VisualTreeHelper.GetChild(parent, i);
if (FindControl<T>(child, targetType, ControlName) != null)
{
result = FindControl<T>(child, targetType, ControlName);
break;
}
}
return result;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.