I am trying to make a custom UserControl (TSFloorPlanProductDispenserStockViewer) to display an error code and a list of buttons for the user to interact with. In a DataTemplate (TSFloorPlanInputStationProductDispenserMemberTemplate), I wish to use two 'instances' of this custom UserControl, but I'm messing up the DataContext but I'm not sure how to set it correctly.
TSFloorPlanProductDispenserStockViewer.xaml:
<UserControl x:Class="ASC_Control_HMI.TSFloorPlanProductDispenserStockViewer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ASC_Control_HMI"
mc:Ignorable="d"
Name="ucProductDispenserStockViewer"
>
<Grid>
<DockPanel>
<Border DockPanel.Dock="Top" Background="LightYellow">
<TextBlock x:Name="tbProductDispenserStockID" FontSize="20" HorizontalAlignment="Center"/>
</Border>
<Border x:Name="brdChangeButtons" Background="LightSalmon" DockPanel.Dock="Bottom" Margin="4,0,4,0" Height="60" BorderBrush="White" CornerRadius="4" BorderThickness="1">
<local:TSRemoteManualControlBar x:Name="DispenserStockManualControlBar" HorizontalAlignment="Center" ManualControlBarActions="{Binding ViewerManualControlBarActions}">
</local:TSRemoteManualControlBar>
</Border>
<Border DockPanel.Dock="Bottom" Background="LightBlue">
<TextBlock x:Name="tbErrorStock" FontSize="16" HorizontalAlignment="Center" Text="{Binding ProductDispenserStockError}"/>
</Border>
</DockPanel>
</Grid>
</UserControl>
TSFloorPlanProductDispenserStockViewer.xaml.cs (extract of):
public static readonly DependencyProperty ProductDispenserStockErrorProperty = DependencyProperty.Register(
"ProductDispenserStockError", typeof(string), typeof(TSFloorPlanProductDispenserStockViewer),
new PropertyMetadata(null, new PropertyChangedCallback(TSFloorPlanProductDispenserStockViewer.OnProductDispenserStockErrorPropertyChanged)));
private static void OnProductDispenserStockErrorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TSFloorPlanProductDispenserStockViewer control = d as TSFloorPlanProductDispenserStockViewer;
if ((control != null) && (e.NewValue != null))
{
//(control.DataContext as TSFloorPlanProductDispenserStockViewer).ProductDispenserStockError = Convert.ToString(e.NewValue);
control.DataContext = e.NewValue;
}
}
public string ProductDispenserStockError
{
get
{
return (string)GetValue(ProductDispenserStockErrorProperty);
}
set
{
SetValue(ProductDispenserStockErrorProperty, value);
}
}
public static readonly DependencyProperty ViewerManualControlBarActionsProperty = DependencyProperty.Register(
"ViewerManualControlBarActions", typeof(ObservableCollection<TSRemoteManualControlBarAction>), typeof(TSFloorPlanProductDispenserStockViewer),
new PropertyMetadata(new PropertyChangedCallback(TSFloorPlanProductDispenserStockViewer.OnManualControlBarActionsPropertyChanged)));
private static void OnManualControlBarActionsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TSFloorPlanProductDispenserStockViewer control = d as TSFloorPlanProductDispenserStockViewer;
if ((control != null) && (e.NewValue != null))
{
control.DataContext = e.NewValue;
}
}
private ObservableCollection<TSRemoteManualControlBarAction> m_ViewerManualControlBarActions;
public ObservableCollection<TSRemoteManualControlBarAction> ViewerManualControlBarActions
{
get
{
return (ObservableCollection<TSRemoteManualControlBarAction>)GetValue(ViewerManualControlBarActionsProperty);
}
set
{
SetValue(ViewerManualControlBarActionsProperty, value);
}
}
TSFloorPlanInputStationProductDispenserMemberTemplate.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ASC_Control_HMI"
>
<local:BoolToOpacityValueConverter x:Key="IsActiveToOpacity"/>
<local:BoolToVisibilityValueConverter x:Key="IsActiveToVisibility"/>
<DataTemplate x:Key="FloorPlanInputStationProductDispenserMemberTemplate" DataType="{x:Type local:TSFloorPlanInputStationProductDispenser}">
<Grid Visibility="{Binding IsVisible, Mode=OneWay, Converter={StaticResource IsActiveToVisibility}}">
<DockPanel>
<DockPanel DockPanel.Dock="Bottom">
<Grid x:Name="grdDispenserStockViewers" Margin="0,10,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="60"/>
<!--<RowDefinition Height="*"/>-->
</Grid.RowDefinitions>
<local:TSFloorPlanProductDispenserStockViewer Grid.Row="0" Grid.Column="0" x:Name="ProductDispenserStockViewer1" ProductDispenserStockID="Upper" ProductDispenserStockError="{Binding UpperStockError}" ViewerManualControlBarActions="{Binding ManualControlBarActions1}">
</local:TSFloorPlanProductDispenserStockViewer>
<local:TSFloorPlanProductDispenserStockViewer Grid.Row="0" Grid.Column="1" x:Name="ProductDispenserStockViewer2" ProductDispenserStockID="Lower" ProductDispenserStockError="{Binding LowerStockError}" ViewerManualControlBarActions="{Binding ManualControlBarActions2}">
</local:TSFloorPlanProductDispenserStockViewer>
</Grid>
</DockPanel>
</DockPanel>
</Grid>
</DataTemplate>
</ResourceDictionary>
I'm setting the error strings to "DefaultError" in the constructor of the TSFloorPlanProductDispenserStockViewer in order to see something at least, but they are printed in the slots reserved for the ManualControlBar. I'm pretty sure I've got the DataContext setup wrong, but I am unable to get it right with the other posts I found concerning binding of data.
Any help would be greatly appreciated
======= EDIT =======
I've removed the Viewer's references to the DataContext, as suggested by @bioniccode, and any default values I give to the viewer's properties ProductDispenserStockError or ViewerManualControlBarActions get displayed. When I try to use the UserControl's parent's properties for these values though, they don't update
<local:TSFloorPlanProductDispenserStockViewer Grid.Row="0" Grid.Column="0"
x:Name="ProductDispenserStockViewer1"
ProductDispenserStockID="Upper"
ProductDispenserStockError="{Binding UpperStockError}"
ViewerManualControlBarActions="{Binding ManualControlBarActions1}">
</local:TSFloorPlanProductDispenserStockViewer>
<local:TSFloorPlanProductDispenserStockViewer Grid.Row="0" Grid.Column="1"
x:Name="ProductDispenserStockViewer2"
ProductDispenserStockID="Lower"
ProductDispenserStockError="{Binding LowerStockError}"
ViewerManualControlBarActions="{Binding ManualControlBarActions2}">
How do I bind the viewers properties to the properties from the Template's 'code-behind'?
You don't set the DataContext internally. You never do this. You don't even get the
DataContext. Your control is able to operate without knowing theDataContext.Internal elements always bind to dependency properties of the custom control (or
UserControl). Whencreating aControlTemplateyou bind using{TemplateBinding}or{Binding RelativeSource={realtiveSource TemplatedParent}}. In every other case you useBinding.ElementNameorBinding.RelativeSource.For example
TSFloorPlanProductDispenserStockViewer.xaml.cs
Remove all references to te DataContext.
TSFloorPlanProductDispenserStockViewer.xaml.cs
Then fix the bindings and bind to directly the dependency properties defined by the
UserControl.Now you can use the control as usual. External DataContext values are no longer overwritten and therefore external bindings set on the control can behave normal.