similar to my Labeled TextBox, which issues are resolved in:
Labeled TextBox in Windows Universal App
I got two issues in my Labeled Combobox, but first the Code:
Generic.xaml:
<Style TargetType="template:LabeledComboBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="template:LabeledComboBox">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="{TemplateBinding Label}" FontWeight="Bold" VerticalAlignment="Center" Margin="10,0" />
<ComboBox x:Name="PART_ComboBox" ItemsSource="{TemplateBinding ItemsSource}" SelectedIndex="{TemplateBinding SelectedIndex}" SelectedValue="{TemplateBinding SelectedValue}" SelectedValuePath="{TemplateBinding SelectedValuePath}" DisplayMemberPath="{TemplateBinding DisplayMemberPath}" VerticalAlignment="Center" Margin="20,0,10,0" Grid.Row="1" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
LabeledComboBox.cs:
[TemplatePart(Name = "PART_ComboBox", Type = typeof(ComboBox))]
public sealed class LabeledComboBox : Control, IParameterReturnable
{
public static readonly DependencyProperty LabelProperty = DependencyProperty.Register("Label", typeof(string), typeof(LabeledComboBox), new PropertyMetadata(""));
public string Label
{
get { return GetValue(LabelProperty).ToString(); }
set { SetValue(LabelProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(object), typeof(LabeledComboBox), new PropertyMetadata(null));
public object ItemsSource
{
get { return GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty SelectedIndexProperty = DependencyProperty.Register("SelectedIndex", typeof(int), typeof(LabeledComboBox), new PropertyMetadata(default(int)));
public int SelectedIndex
{
get { return (int) GetValue(SelectedIndexProperty); }
set { SetValue(SelectedIndexProperty, value); }
}
public static readonly DependencyProperty SelectedValueProperty = DependencyProperty.Register("SelectedValue", typeof(object), typeof(LabeledComboBox), new PropertyMetadata(null));
public object SelectedValue
{
get { return GetValue(SelectedValueProperty); }
set { SetValue(SelectedValueProperty, value); }
}
public static readonly DependencyProperty SelectedValuePathProperty = DependencyProperty.Register("SelectedValuePath", typeof(string), typeof(LabeledComboBox), new PropertyMetadata(default(string)));
public string SelectedValuePath
{
get { return GetValue(SelectedValuePathProperty).ToString(); }
set { SetValue(SelectedValuePathProperty, value); }
}
public static readonly DependencyProperty DisplayMemberPathProperty = DependencyProperty.Register("DisplayMemberPath", typeof(string), typeof(LabeledComboBox), new PropertyMetadata(default(string)));
public string DisplayMemberPath
{
get { return GetValue(DisplayMemberPathProperty).ToString(); }
set { SetValue(DisplayMemberPathProperty, value); }
}
private ComboBox _comboBox;
public LabeledComboBox()
{
this.DefaultStyleKey = typeof(LabeledComboBox);
}
public LabeledComboBox(List<Parameter> parameterList)
{
this.Label = parameterList[0].DisplayName ?? "";
this.ItemsSource = parameterList;
this.SelectedValuePath = "DefaultValue";
this.DisplayMemberPath = "DefaultValue";
this.SelectedIndex = 0;
this.DefaultStyleKey = typeof(LabeledComboBox);
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
_comboBox = GetTemplateChild("PART_ComboBox") as ComboBox;
if (_comboBox != null)
{
_comboBox.SelectionChanged += OnComboBoxSelectionChanged;
if (_comboBox.Items != null)
{
this.SelectedIndex = 0;
_comboBox.SelectedValue = _comboBox.Items[this.SelectedIndex];
}
}
}
private void OnComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
this.SelectedValue = _comboBox.SelectedValue;
}
public string GetKey()
{
return Label;
}
public string GetValue()
{
return SelectedValue.ToString();
}
}
It will be called in two different ways:
Dynamically in C#:
stackPanel.Add(new LabeledComboBox(parameterList));
Static in Xaml:
<templates:LabeledComboBox Label="Kategorien:" ItemsSource="{Binding ElementName=pageRoot, Path=FeedCategories}" DisplayMemberPath="Name" SelectedValuePath="Name" />
As I said before I got two issues with it:
How can I bind the SelectionChangedEvent to access it in Xaml || C#
As you can see, I try to preselect the first Item, which does not work and I don't know how to do it right
Thank you very much for all helpful and well meant answers in advance!
Instead of creating a custom control and recreating all needed dependency properties, I would suggest you use the Header and HeaderTemplate properties of the built in ComboBox, which will be displayed, just like in your LabeledComboBox, above the selection menu. Additionally the SelectionChanged event will be available.
So the usage in XAML would look like the following:
<ComboBox
DisplayMemberPath="Name"
Header="Kategorien:"
ItemsSource="{Binding ElementName=pageRoot, Path=FeedCategories}"
SelectedValuePath="Name"
SelectionChanged="OnSelectionChanged">
<ComboBox.HeaderTemplate>
<DataTemplate>
<TextBlock
Margin="10,0"
VerticalAlignment="Center"
FontWeight="Bold"
Text="{Binding}" />
</DataTemplate>
</ComboBox.HeaderTemplate>
</ComboBox>
But if you don't want to use the above method, to expose the selection changed event in your LabeledComboBox, add the following code:
private void OnComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
this.SelectedValue = _comboBox.SelectedValue;
this.RaiseSelectionChanged(e);
}
public event EventHandler<SelectionChangedEventArgs> SelectionChanged;
private void RaiseSelectionChanged(SelectionChangedEventArgs args)
{
if (SelectionChanged != null)
{
SelectionChanged(this, args);
}
}
Then you can use the created SelectionChanged event from XAML.
Related
This question already has answers here:
XAML binding not working on dependency property?
(1 answer)
Callback when dependency property recieves xaml change
(2 answers)
Closed 9 months ago.
I'm trying to make a user control in WPF (using xceed). I have a combobox and an integerUpDown.
A label should change its content when one of those changes its value. First label has just a simple binding to the integerUpDown, that's working. Visual Studio created OnPropertyChanged() and getters/setters automatically and I try to use it to bind my result to the second label but it's not working. Nothing happens.
This is my XAML:
<UserControl x:Class="WpfApp2.CardSelectionControl"
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:WpfApp2" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel Margin="5" Width="300" Height="400" Background="LightGray">
<TextBox Margin="5" AcceptsReturn="True" FontSize="20" Padding="5" Height="70">placeholding</TextBox>
<ComboBox SelectedIndex="{Binding SelectedIndex}" Background="Red" Margin="5">
<ComboBoxItem Content="card1"></ComboBoxItem>
<ComboBoxItem Content="card2"></ComboBoxItem>
</ComboBox>
<xctk:UIntegerUpDown Margin="5" x:Name="upDown" Value="{Binding Level}" ></xctk:UIntegerUpDown>
<Label Height="50" Content="{Binding Level}"></Label>
<Label Height="50" Content="{Binding Result}" ></Label>
</StackPanel>
</UserControl>
Code behind:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace WpfApp2
{
public partial class CardSelectionControl : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int Level
{
get { OnPropertyChanged(); return (int)GetValue(LevelProperty); }
set
{
OnPropertyChanged();
SetValue(LevelProperty, value);
}
}
public int SelectedIndex
{
get { OnPropertyChanged(); return (int)GetValue(SelectedIndexProperty); }
set
{
OnPropertyChanged();
SetValue(SelectedIndexProperty, value);
}
}
public int Result
{
get { return (int)GetValue(ResultProperty); }
set { SetValue(ResultProperty, value); }
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
if (SelectedIndex == 0)
{ Result = Level * 2; }
else if (SelectedIndex == 1)
{ Result = Level * 3; }
}
public static readonly DependencyProperty ResultProperty =
DependencyProperty.Register("Result", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(20));
public static readonly DependencyProperty LevelProperty =
DependencyProperty.Register("Level", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(10));
public static readonly DependencyProperty SelectedIndexProperty =
DependencyProperty.Register("SelectedIndex", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(0));
public CardSelectionControl()
{
DataContext = this;
InitializeComponent();
}
}
}
I assume you expect your setters to get called, hence why you are calling OnPropertyChanged everywhere. They don't get called so your code won't be executed.
Instead, you can add a callback to your dependency properties so you know when the values get changed:
public static readonly DependencyProperty LevelProperty =
DependencyProperty.Register("Level",
typeof(int),
typeof(CardSelectionControl),
new PropertyMetadata(10, new PropertyChangedCallback(OnChanged)));
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
private static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var cardSelectionControl = (CardSelectionControl)d;
cardSelectionControl.Recalculate();
}
I took the liberty of changing OnPropertyChanged to Recalculate, added a PropertyChangedCallback for the combobox as well and the entire code becomes:
public partial class CardSelectionControl : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int Level
{
get { return (int)GetValue(LevelProperty); }
set { SetValue(LevelProperty, value); }
}
public int SelectedIndex
{
get { return (int)GetValue(SelectedIndexProperty); }
set { SetValue(SelectedIndexProperty, value); }
}
public int Result
{
get { return (int)GetValue(ResultProperty); }
set { SetValue(ResultProperty, value); }
}
public void Recalculate()
{
if (SelectedIndex == 0)
Result = Level * 2;
else if (SelectedIndex == 1)
Result = Level * 3;
}
public static readonly DependencyProperty ResultProperty =
DependencyProperty.Register("Result", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(20));
public static readonly DependencyProperty LevelProperty =
DependencyProperty.Register("Level", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(10, new PropertyChangedCallback(OnChanged)));
public static readonly DependencyProperty SelectedIndexProperty =
DependencyProperty.Register("SelectedIndex", typeof(int), typeof(CardSelectionControl), new PropertyMetadata(0, new PropertyChangedCallback(OnChanged)));
private static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var cardSelectionControl = (CardSelectionControl)d;
cardSelectionControl.Recalculate();
}
public CardSelectionControl()
{
DataContext = this;
InitializeComponent();
}
}
I want to bind properties from CustomControl to my page, then back to CustomControl and calculate quantity of pages and display it in list.
My code look like.
CustomControl
public partial class CustomControl : UserControl
public CustomControl()
{
InitializeComponent()
}
public int PageSelected
{
get
{
return (int)GetValue(PageSelectedProperty);
}
set
{
SetValue(PageSelectedProperty, value);
}
}
public static readonly DependencyProperty PageSelectedProperty = DependencyProperty.Register("PageSelected", typeof(int), typeof(CustomControl), new PropertyMetadata(null));
public int RecordsPerPage
{
get
{
return (int)GetValue(RecordsPerPageProperty);
}
set
{
SetValue(RecordsPerPageProperty, value);
}
}
public static readonly DependencyProperty RecordsPerPageProperty = DependencyProperty.Register("RecordsPerPage", typeof(int), typeof(CustomControl), new PropertyMetadata(null));
public IList<int> RecordsPerPageList
{
get
{
return (IList<int>)GetValue(RecordsPerPageListProperty);
}
set
{
SetValue(RecordsPerPageListProperty, value);
}
}
public static readonly DependencyProperty RecordsPerPageListProperty = DependencyProperty.Register("RecordsPerPageList", typeof(List<int>), typeof(CustomControl), new PropertyMetadata(null));
public int RecordsCount
{
get
{
return (int)GetValue(RecordsCountProperty);
}
set
{
SetValue(RecordsCountProperty, value);
CreatePagesList();
}
}
public static readonly DependencyProperty RecordsCountProperty = DependencyProperty.Register("RecordsCount", typeof(int), typeof(CustomControl), new PropertyMetadata(null));
public IList<int> PagesList
{
get
{
return (IList<int>)GetValue(PagesListProperty);
}
set
{
SetValue(PagesListProperty, value);
}
}
public static readonly DependencyProperty PagesListProperty = DependencyProperty.Register("PagesList", typeof(List<int>), typeof(CustomControl), new PropertyMetadata(null));
public int PagesCount
{
get
{
return (int)GetValue(PagesCountProperty);
}
set
{
SetValue(PagesCountProperty, value);
}
}
public static readonly DependencyProperty PagesCountProperty = DependencyProperty.Register("PagesCount", typeof(int), typeof(CustomControl), new PropertyMetadata(null));
Custom Control xaml
<UserControl x:Class="Mtrx.CustomControls.CustomControl"
mc:Ignorable="d"
d:DesignHeight="90" Width="200">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="90" />
</Grid.RowDefinitions>
<ComboBox Width="40" Height="20" Grid.Column="1" Margin="0,5,0,5"
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=PagesList}"
SelectedItem="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=PageSelected, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
HorizontalContentAlignment="Center"/>
<ComboBox Width="40" Height="20" Grid.Column="9" Margin="0,5,0,5"
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=RecordsPerPageList, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
SelectedItem="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=RecordsPerPage, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
HorizontalContentAlignment="Center"/>
</Grid>
</UserControl>
Page xaml.cs
public class PageVievModel:AbstractPage
public int RowsCount
{
get
{
return _rowsCount;
}
set
{
if (value != _rowsCount)
{
_rowsCount = value;
RaisePropertyChanged("RowsCount");
}
}
}
private int _rowsCount; //we get it from other place
public int DGRecordsMax
{
get
{
return _dgRecordsMax;
}
set
{
if (value != _dgRecordsMax)
{
_dgRecordsMax = value;
if (value > 0)
{
DataGridRecordsMaxCount = value.ToString();
Settings.Default.Save();
}
RaisePropertyChanged("DGRecordsMax");
}
}
}
private int _dgRecordsMax;
public IList<int> DGRecordsMaxList
{
get
{
return _dGRecordsMaxList;
}
set
{
if (_dGRecordsMaxList != value)
{
_dGRecordsMaxList = value;
RaisePropertyChanged("DGRecordsMaxList");
}
}
}
private IList<int> _dGRecordsMaxList = new List<int>();
public IList<int> PagesList
{
get
{
return _pagesList;
}
set
{
if (_pagesList != value)
{
_pagesList = value;
RaisePropertyChanged("PagesList");
}
}
}
private IList<int> _pagesList = new List<int>();
public int PagesCount
{
get
{
return _pagesCount;
}
set
{
if (value != _pagesCount)
{
_pagesCount = value;
RaisePropertyChanged("PagesCount");
}
}
}
private int _pagesCount;
public IList<int> CurrentPageList
{
get
{
return _currentPageList;
}
set
{
if (_currentPageList != value)
{
_currentPageList = value;
RaisePropertyChanged("CurrentPageList");
}
}
}
private IList<int> _currentPageList;
Page xaml
<UserControl x:Class="SomeClass"
mc:Ignorable="d"
d:DesignHeight="400" d:DesignWidth="800"
IsEnabled="{Binding AllowInput, Converter={StaticResource AnyToBooleanConverter}}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DockPanel>
<SomeClass:CustomControl Width="280" Height="190"
RecordsCount="{Binding RowsCount, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
RecordsPerPage="{Binding DGRecordsMax, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay }"
RecordsPerPageList="{Binding DGRecordsMaxList, Mode=TwoWay}"
PagesCount="{Binding PagesCount, Mode=TwoWay}"
PageSelected="{Binding CurrentPage, Mode=TwoWay}"
PagesList="{Binding PagesList, Mode=TwoWay}"
RecordsFrom="{Binding RecordsFrom, Mode=TwoWay}"
RecordsTo="{Binding RecordsTo, Mode=TwoWay}"
DockPanel.Dock="Right"
VerticalAlignment="Bottom"/>
</WrapPanel.Resources>
</WrapPanel>
</Grid>
</UserControl>
When I am trying to run my program there are empty lists, earlier when I just kept more properties in Page it was working fine.
I would be greatful for help. It's hard to understand for me how make this two way bindable properties.
I tried to make an example using your code.
For me it worked if I changed both IList and List to ObservableCollection, e.g.
using System.Collections.ObjectModel;
....
public ObservableCollection<int> PagesList
{
get
{
return (ObservableCollection<int>)GetValue ( PagesListProperty );
}
set
{
SetValue ( PagesListProperty, value );
}
}
public static readonly DependencyProperty PagesListProperty = DependencyProperty.Register("PagesList", typeof(ObservableCollection<int>), typeof(CustomControl), new PropertyMetadata(null));
Note that I changed both the Property definition and the DependencyProperty definition.
Your code is a bit messy, for example you have a DockPanel tag which is closed with /WrapPanel, which obviously will not compile.
<DockPanel>
</WrapPanel>
I have a sample program to recreate the issue I am having.
Control:
public class PropertyUpdateControl : Control
{
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
nameof(Value), typeof(double), typeof(PropertyUpdateControl),
new FrameworkPropertyMetadata(default(double), FrameworkPropertyMetadataOptions.None, ValueProperty_Changed));
public double Value
{
get { return (double)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
private static void ValueProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var lPropertyUpdateControl = (PropertyUpdateControl) d;
lPropertyUpdateControl.Value_Changed((double)e.OldValue, (double)e.NewValue);
}
private void Value_Changed(double oldValue, double newValue)
{
this.Value = newValue;
this.Label = newValue.ToString();
}
public static readonly DependencyProperty LabelProperty = DependencyProperty.Register(
nameof(Label), typeof(string), typeof(PropertyUpdateControl), new PropertyMetadata("Label"));
public string Label
{
get { return (string)GetValue(LabelProperty); }
set { SetValue(LabelProperty, value); }
}
}
Main Window
public class MainWindowViewModel : ViewModelBase
{
private PropertyUpdateViewModel mPropertyUpdateViewModel;
public MainWindowViewModel()
{
this.PropertyUpdateViewModel = new PropertyUpdateViewModel();
this.ChangeCommand = new RelayCommand(this.ChangeImplementation);
}
public ICommand ChangeCommand { get; set; }
public void ChangeImplementation()
{
if (this.PropertyUpdateViewModel == null)
{
this.PropertyUpdateViewModel = new PropertyUpdateViewModel();
}
else
{
this.PropertyUpdateViewModel = null;
}
}
public PropertyUpdateViewModel PropertyUpdateViewModel
{
get { return this.mPropertyUpdateViewModel;}
set { this.Set(ref this.mPropertyUpdateViewModel, value); }
}
}
public class PropertyUpdateViewModel : ViewModelBase
{
private double mValue;
private static int sInstanceCounter = 0;
public PropertyUpdateViewModel()
{
Interlocked.Increment(ref sInstanceCounter);
this.Value = sInstanceCounter;
}
public double Value
{
get { return this.mValue; }
set { this.Set(ref this.mValue, value); }
}
}
XAML
<Window.Resources>
<local:MainWindowViewModel x:Key="mKeyMainWindowViewModel" />
<Style TargetType="{x:Type local:PropertyUpdateControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:PropertyUpdateControl}">
<TextBlock
Margin="25" Text="{TemplateBinding Label}"></TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate x:Key="mDataTemplate" DataType="{x:Type local:PropertyUpdateViewModel}">
<local:PropertyUpdateControl
Value="{Binding Value}"></local:PropertyUpdateControl>
</DataTemplate>
</Window.Resources>
<Window.DataContext>
<StaticResource ResourceKey="mKeyMainWindowViewModel"/>
</Window.DataContext>
<Grid>
<StackPanel>
<ContentPresenter Content="{Binding PropertyUpdateViewModel}" ContentTemplate="{StaticResource mDataTemplate}"/>
<Button Content="Update Value" Command="{Binding ChangeCommand}"></Button>
</StackPanel>
</Grid>
So basically the control seems to only be updated once. The label will always read 1 even after setting the view model to null and then to a new instance of the PropertyUpdateViewModel which will have an incrementing value. Why is the PropertyUpdateControl Value_Changed only called once and never called again even after the datacontext changes?
I found the issue to be in
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
nameof(Value), typeof(double), typeof(PropertyUpdateControl),
new FrameworkPropertyMetadata(default(double), FrameworkPropertyMetadataOptions.None, ValueProperty_Changed));
It needs to be FrameworkPropertyMetadataOptions.BindsTwoWayByDefault.
This post has more info on binding for anyone interested https://www.tutorialspoint.com/wpf/wpf_data_binding.htm
I built a custom control TabularListView based on ListView with a new Property, an ObservableCollection of TabularListViewHeaderColumn.
TabularListView:
class TabularListView : ListView
{
public TabularListView()
{
Columns = new ObservableCollection<TabularListViewHeaderColumn>();
}
public ObservableCollection<TabularListViewHeaderColumn> Columns
{
get { return (ObservableCollection<TabularListViewHeaderColumn>)GetValue(ColumnsProperty); }
set { SetValue(ColumnsProperty, value); }
}
public static readonly DependencyProperty ColumnsProperty =
DependencyProperty.Register("Columns", typeof(ObservableCollection<TabularListViewHeaderColumn>), typeof(TabularListView), new PropertyMetadata(new ObservableCollection<TabularListViewHeaderColumn>()));
}
TabularListViewHeaderColumn:
class TabularListViewHeaderColumn : ContentControl
{
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(TabularListViewHeaderColumn), new PropertyMetadata(string.Empty));
public string MappingName
{
get { return (string)GetValue(MappingNameProperty); }
set { SetValue(MappingNameProperty, value); }
}
public static readonly DependencyProperty MappingNameProperty =
DependencyProperty.Register("MappingName", typeof(string), typeof(TabularListViewHeaderColumn), new PropertyMetadata(string.Empty));
}
I try to use it like shown below, setting the TabularListViewHeaderColumns directly in XAML:
<Controls:TabularListView>
<Controls:TabularListView.Header>
<ItemsControl>
<DataTemplate>
<Button Content="{Binding Title}" Width="{Binding Width}"
FontWeight="Bold"/>
</DataTemplate>
</ItemsControl>
</Controls:TabularListView.Header>
<Controls:TabularListView.Columns>
<Controls:TabularListViewHeaderColumn Title="Test" MappingName="Inactive" Width="60"/>
<Controls:TabularListViewHeaderColumn Title="Articlenumber" MappingName="DeviceType.ArticleNo" Width="170"/>
<Controls:TabularListViewHeaderColumn Title="Serialnumber" MappingName="SerialNo" Width="170"/>
<Controls:TabularListViewHeaderColumn Title="Inventorynumber" MappingName="EndCustNo" Width="170"/>
<Controls:TabularListViewHeaderColumn Title="Description" MappingName="DeviceType.Description1" Width="300"/>
</Controls:TabularListView.Columns>
</Controls:TabularListView>
Usualy when creating a regular Custom Control like in this example https://social.technet.microsoft.com/wiki/contents/articles/32828.uwp-how-to-create-and-use-custom-control.aspx , I need to bind the respected property to the DependencyProperty, but since I only inherit from ListView and don't have any Xaml, I'm quiet confused how to do so.
All:
The more I search for solutions to this question the more confused I become. After spending 12-16 hours watching YouTube, reading StackOverflow and general goggling, I thought I'd plead for additional help.
I would like to create a custom control so I can write various apps to remote to my video switcher.
I've created my control with dependency properties and that part is working well.
This answer on SO seemed to get me close, but I still can't get my app to run.
How to wire up a click event for a custom usercontrol button? Should I use CustomControl?
What I simply want to do is click btnIn1 in the control and have it return a "1", btnIn2 returns a "2" and so on.
I've also read about delegates, ICommand, TemplateParts and MVVM patterns which all seem like incredibly complex ways to click a button within a group. Maybe there's just not a simple way to do it.
Here's what I have so far. I simplified everything to a 2x2 matrix switcher (rather than the 4x4 I'm working on)
Thanks for all your help.
Norm
Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:VideoSwitcher"
xmlns:enk="clr-namespace:VideoSwitcher.Controls">
<Style TargetType="{x:Type enk:Matrix44}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type enk:Matrix44}">
<Grid x:Name="grdBase" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25*"/>
<ColumnDefinition Width="25*"/>
<ColumnDefinition Width="25*"/>
<ColumnDefinition Width="25*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40*"/>
<RowDefinition Height="100*"/>
<RowDefinition Height="40*"/>
<RowDefinition Height="100*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label x:Name="lblInputHeader" Content="Input"
Grid.Column="0"
Grid.Row="0"
Grid.ColumnSpan="4"
Visibility="{TemplateBinding HeaderVisible}"
FontFamily="{TemplateBinding HeaderFont}"
FontSize="{TemplateBinding HeaderFontSize}"/>
<StackPanel Orientation="Horizontal"
Grid.Column="0"
Grid.Row="1"
Grid.ColumnSpan="4">
<Button x:Name="btnIn1"
Margin="{TemplateBinding ButtonMargin}"
Height="{TemplateBinding ButtonHeight}"
Width="{TemplateBinding ButtonWidth}"
Content="{TemplateBinding Input1Label}"
Click="btnIn1Click"/>
<Button x:Name="btnIn2"
Margin="{TemplateBinding ButtonMargin}"
Height="{TemplateBinding ButtonHeight}"
Width="{TemplateBinding ButtonWidth}"
Content="{TemplateBinding Input2Label}"
Click="btnIn2Click"/>
</StackPanel>
<Label x:Name="lblOutputHeader" Content="Output"
Grid.Column="0"
Grid.Row="2"
Grid.ColumnSpan="4"
Visibility="{TemplateBinding HeaderVisible}"
FontFamily="{TemplateBinding HeaderFont}"
FontSize="{TemplateBinding HeaderFontSize}"/>
<StackPanel Orientation="Horizontal"
Grid.Column="0"
Grid.Row="3"
Grid.ColumnSpan="4">
<Button x:Name="btnOut1"
Margin="{TemplateBinding ButtonMargin}"
Height="{TemplateBinding ButtonHeight}"
Width="{TemplateBinding ButtonWidth}"
Content="{TemplateBinding Output1Label}"
Click="btnOut1Click"/>
<Button x:Name="btnOut2"
Margin="{TemplateBinding ButtonMargin}"
Height="{TemplateBinding ButtonHeight}"
Width="{TemplateBinding ButtonWidth}"
Content="{TemplateBinding Output2Label}"
Click="btnOut2Click"/>
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Custom Control (Matrix44.cs)
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.ComponentModel;
namespace VideoSwitcher.Controls
{
public class Matrix44 : Control
{
#region Events - Go here if I can ever find out how to use them
#endregion
//This does not work --------
public event RoutedEventHandler Click;
void btnIn1Click(object sender, RoutedEventArgs e)
{
if (this.Click != null)
{
this.Click(this, e);
}
}
void btnIn2Click(object sender, RoutedEventArgs e)
{
if (this.Click != null)
{
this.Click(this, e);
}
}
void btnOut1Click(object sender, RoutedEventArgs e)
{
if (this.Click != null)
{
this.Click(this, e);
}
}
void btnOut2Click(object sender, RoutedEventArgs e)
{
if (this.Click != null)
{
this.Click(this, e);
}
}
#region Properties - Exposed to the user in the Properties panel, XAML or code-behind
#region Switcher Appearance Properies (Height, Width, Margin)
[Category("Switcher Appearance Properties")]
public double ButtonHeight
{
get { return (double)GetValue(ButtonHeightProperty); }
set { SetValue(ButtonHeightProperty, value); }
}
public static readonly DependencyProperty ButtonHeightProperty =
DependencyProperty.Register(nameof(ButtonHeight), typeof(double), typeof(Matrix44), new PropertyMetadata(50.0));
[Category("Switcher Appearance Properties")]
public double ButtonWidth
{
get { return (double)GetValue(ButtonWidthProperty); }
set { SetValue(ButtonWidthProperty, value); }
}
public static readonly DependencyProperty ButtonWidthProperty =
DependencyProperty.Register(nameof(ButtonWidth), typeof(double), typeof(Matrix44), new PropertyMetadata(50.0));
[Category("Switcher Appearance Properties")]
public Thickness ButtonMargin
{
get { return (Thickness)GetValue(ButtonMarginProperty); }
set { SetValue(ButtonMarginProperty, value); }
}
public static readonly DependencyProperty ButtonMarginProperty =
DependencyProperty.Register("ButtonMargin", typeof(Thickness), typeof(Matrix44));
#endregion
#region Labels
[Category("Switcher Label Properties")]
public string Input1Label
{
get { return (string)GetValue(Input1LabelProperty); }
set { SetValue(Input1LabelProperty, value); }
}
public static readonly DependencyProperty Input1LabelProperty =
DependencyProperty.Register(nameof(Input1Label), typeof(string), typeof(Matrix44), new PropertyMetadata("Input 1"));
[Category("Switcher Label Properties")]
public string Input2Label
{
get { return (string)GetValue(Input2LabelProperty); }
set { SetValue(Input2LabelProperty, value); }
}
public static readonly DependencyProperty Input2LabelProperty =
DependencyProperty.Register(nameof(Input2Label), typeof(string), typeof(Matrix44), new PropertyMetadata("Input 2"));
[Category("Switcher Label Properties")]
public string Output1Label
{
get { return (string)GetValue(Output1LabelProperty); }
set { SetValue(Output1LabelProperty, value); }
}
public static readonly DependencyProperty Output1LabelProperty =
DependencyProperty.Register(nameof(Output1Label), typeof(string), typeof(Matrix44), new PropertyMetadata("Output 1"));
[Category("Switcher Label Properties")]
public string Output2Label
{
get { return (string)GetValue(Output2LabelProperty); }
set { SetValue(Output2LabelProperty, value); }
}
public static readonly DependencyProperty Output2LabelProperty =
DependencyProperty.Register(nameof(Output2Label), typeof(string), typeof(Matrix44), new PropertyMetadata("Output 2"));
#endregion
#region Header Properties
[Category("Switcher Header Properties")]
public Visibility HeaderVisible
{
get { return (Visibility)GetValue(HeaderVisibleProperty); }
set { SetValue(HeaderVisibleProperty, value); }
}
public static readonly DependencyProperty HeaderVisibleProperty =
DependencyProperty.Register("HeaderVisible", typeof(Visibility), typeof(Matrix44));
[Category("Switcher Header Properties")]
public FontFamily HeaderFont
{
get { return (FontFamily)GetValue(HeaderFontProperty); }
set { SetValue(HeaderFontProperty, value); }
}
public static readonly DependencyProperty HeaderFontProperty =
DependencyProperty.Register("HeaderFont", typeof(FontFamily), typeof(Matrix44));
[Category("Switcher Header Properties")]
public double HeaderFontSize
{
get { return (double)GetValue(HeaderFontSizeProperty); }
set { SetValue(HeaderFontSizeProperty, value); }
}
public static readonly DependencyProperty HeaderFontSizeProperty =
DependencyProperty.Register("HeaderFontSize", typeof(double), typeof(Matrix44));
#endregion
#region Channel Properties
[Category("Switcher Channel Properties")]
//Channel Property - use to extend switcher tool capabilties; e.g. add new bank of ins/outs and remap input 1 to input 5 on 2nd bank
public int Input1Channel
{
get { return (int)GetValue(Input1ChannelProperty); }
set { SetValue(Input1ChannelProperty, value); }
}
public static readonly DependencyProperty Input1ChannelProperty =
DependencyProperty.Register("Input1Channel", typeof(int), typeof(Matrix44), new PropertyMetadata(1));
[Category("Switcher Channel Properties")]
public bool Input1Enabled
{
get { return (bool)GetValue(Input1EnabledProperty); }
set { SetValue(Input1EnabledProperty, value); }
}
public static readonly DependencyProperty Input1EnabledProperty =
DependencyProperty.Register("Input1Enabled", typeof(bool), typeof(Matrix44), new PropertyMetadata(false));
[Category("Switcher Channel Properties")]
public int Input2Channel
{
get { return (int)GetValue(Input2ChannelProperty); }
set { SetValue(Input2ChannelProperty, value); }
}
public static readonly DependencyProperty Input2ChannelProperty =
DependencyProperty.Register("Input2Channel", typeof(int), typeof(Matrix44), new PropertyMetadata(2));
[Category("Switcher Channel Properties")]
public bool Input2Enabled
{
get { return (bool)GetValue(Input1EnabledProperty); }
set { SetValue(Input1EnabledProperty, value); }
}
[Category("Switcher Channel Properties")]
public int Output1Channel
{
get { return (int)GetValue(Output1ChannelProperty); }
set { SetValue(Output1ChannelProperty, value); }
}
//Output channels
public static readonly DependencyProperty Output1ChannelProperty =
DependencyProperty.Register("Output1Channel", typeof(int), typeof(Matrix44), new PropertyMetadata(1));
[Category("Switcher Channel Properties")]
public int Output2Channel
{
get { return (int)GetValue(Output2ChannelProperty); }
set { SetValue(Output2ChannelProperty, value); }
}
public static readonly DependencyProperty Output2ChannelProperty =
DependencyProperty.Register("Output2Channel", typeof(int), typeof(Matrix44), new PropertyMetadata(2));
#endregion
#endregion
public Matrix44()
{
DefaultStyleKey = typeof(Matrix44);
}
}
}
MainWindow.XAML
<Window
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:VideoSwitcher"
xmlns:Controls="clr-namespace:VideoSwitcher.Controls" x:Class="VideoSwitcher.MainWindow"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="200">
<Grid Margin="0,1,0,0">
<Controls:Matrix44 x:Name="swtMatrix"
HorizontalAlignment="Left"
Margin="10,10,0,0"
VerticalAlignment="Top"
ButtonHeight="65"
ButtonMargin="4"
ButtonWidth="65"/>
<Button x:Name="btnTake"
Content="Take"
HorizontalAlignment="Left"
Margin="10,213,0,0"
VerticalAlignment="Top"
Width="146"
Height="45" Click="btnTake_Click"/>
</Grid>
MainWindow.xaml.cs
using System.Windows;
namespace VideoSwitcher
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public int VideoInputChannel { get; set; }
public int VideoOutputChannel { get; set; }
public MainWindow()
{
InitializeComponent();
AddLabelsToMatrix();
}
public void AddLabelsToMatrix()
{
swtMatrix.Input1Label = "DVR1";
swtMatrix.Input2Label = "DVR2";
swtMatrix.Output1Label = "Videowall";
swtMatrix.Output2Label = "US Right";
}
private void btnTake_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Input channel: " + VideoInputChannel + " routed to Output: " + VideoOutputChannel);
}
/* switcher button psuedo-code
btnIn1_Click (object sender, RoutedEventArgs e)
{
//get input channel of matrix switcher
VideoInputChannel = swtMatrix.btnIn1.Channel;
}
btnIn2_Click (object sender, RoutedEventArgs e)
{
//get input channel of matrix switcher
VideoInputChannel = swtMatrix.btnIn2.Channel;
}
btnOut1_Click (object sender, RoutedEventArgs e)
{
//get output channel of matrix switcher
VideoInputChannel = swtMatrix.btnIn1.Channel;
}
btnOut2_Click (object sender, RoutedEventArgs e)
{
//get output channel of matrix switcher
VideoInputChannel = swtMatrix.btnIn2.Channel;
}
*/
}
}
You could override the OnApplyTemplate() method to get a reference to each Button and then hook up the event handlers:
public class Matrix44 : Control
{
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
Button btnOut1 = this.Template.FindName("btnOut1", this) as Button;
if (btnOut1 != null)
btnOut1.Click += btnIn1Click;
//...and so on for each Button
}
}
You could then either raise a specific event for each Button or define a custom EventArgs that can be used to identify which Button that was clicked in an event handler:
C# event with custom arguments