WPF - Binding in custom ComboBox - c#

I'm trying to create a custom combobox that have a list of items, and each item have an add(+) button that is suppose to add that item to a "favorite" list:
XAML:
<UserControl x:Class=ComboBoxWithButton"
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"
mc:Ignorable="d" d:DesignWidth="300" Height="25">
<ComboBox
x:Name="ComboBoxBtn"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Margin="0,0,0,-1"
Width="300"
ItemsSource="{Binding Source, RelativeSource={RelativeSource AncestorType=UserControl}}"
SelectedItem="{Binding Path=Selected, Mode=TwoWay, RelativeSource={RelativeSource AncestorType=UserControl}}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Content="{Binding}" Width="250" />
<Button Grid.Column="1" Command="{Binding CommandButton}"
CommandParameter="{Binding Path=Selected}">+</Button>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</UserControl>
XAML.CS:
public IEnumerable Source
{
get { return (IEnumerable)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register("Source", typeof(IEnumerable), typeof(ComboBoxWithButton), new PropertyMetadata(null));
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("CommandButton", typeof(ICommand), typeof(ComboBoxWithButton), new PropertyMetadata(null));
public ICommand CommandButton
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
Then on my main view, using the combobox I have:
<controls:ComboBoxWithButton Source="{Binding AvailableClients}" Selected="{Binding SelectedClient, Mode=TwoWay}"
LostFocus="OnClientSelected"
CommandButton="{Binding AddFavoriteCommand}"/>
And:
AddFavoriteCommand = new RelayCommand<object>(AddToFavorite, f => true);
But it's not triggering my function "AddToFavorite"

The button is inside a DataTemplate, so each button's DataContext is different than the UserControl's DataContext.
You need to change the Command binding to access the UserControl's DataContext:
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Content="{Binding}" Width="250" />
<Button Grid.Column="1" Command="{Binding CommandButton, RelativeSource={RelativeSource AncestorType=UserControl}}"
CommandParameter="{Binding Path=Selected}">+</Button>
</Grid>
</DataTemplate>

Related

UserControls aren't displayed properly in ListBox when added from Code-behind (C#, WPF)

I got a simple UserControl that is basically just a Grid with 6 Columns and a bunch of TextBlocks.
XAML:
<UserControl x:Class="MyApplication.TimeAccountItem"
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:MyApplication"
mc:Ignorable="d">
<UserControl.Resources>
<local:TimeSpanConverter x:Key="TimeSpanConverter"/>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="14"/>
<Setter Property="Margin" Value="10,5"/>
<Setter Property="TextAlignment" Value="Center"/>
</Style>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="48"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" x:Name="tb_Id" Text="{Binding User, FallbackValue=0}"/>
<TextBlock Grid.Column="1" x:Name="tb_Employee" Text="{Binding Alias, FallbackValue=Employee}"/>
<TextBlock Grid.Column="2" x:Name="tb_Updated" Text="{Binding Updated, StringFormat=dd.MM.yyy, FallbackValue=00.00.0000}"/>
<TextBlock Grid.Column="3" x:Name="tb_Total" Text="{Binding Total, Converter={StaticResource TimeSpanConverter}, FallbackValue=00:00 h}"/>
<TextBlock Grid.Column="4" x:Name="tb_Claimed" Text="{Binding Claimed, Converter={StaticResource TimeSpanConverter}, FallbackValue=00:00 h}"/>
<TextBlock Grid.Column="5" x:Name="tb_Remaining" Text="{Binding Remaining, Converter={StaticResource TimeSpanConverter}, FallbackValue=00:00 h}"/>
</Grid>
</UserControl>
CS:
public partial class TimeAccountItem : UserControl
{
public TimeAccount content { get; set; }
public OvertimeListBoxItem(TimeAccount timeAccount)
{
content = timeAccount;
this.DataContext = content;
}
public OvertimeListBoxItem()
{
InitializeComponent();
}
}
On my MainWindow I got a ListBox where I want to "output" those UserControls.
When I do this in XAML everything works fine.
<ListBox Height="400" Margin="10,0" HorizontalContentAlignment="Stretch" BorderThickness="0" Name="listbox">
<local:TimeAccountItem/>
<local:TimeAccountItem/>
<local:TimeAccountItem/>
<local:TimeAccountItem/>
<local:TimeAccountItem/>
</ListBox>
However not from Code-behind
foreach (TimeAccount ta in timeAccountList)
{
listbox.Items.Add(new TimeAccountItem(ta));
}
I hovered one of the Items to show what happens. Already gave my UserControl a fixed Height instead of Auto but this didn't help either.
What am I doing wrong here?
Thanks in advance!
The UserControl constructor with TimeAccount argument is missing an InitializeComponent call:
public TimeAccountItem(TimeAccount timeAccount)
{
DataContext = timeAccount;
InitializeComponent();
}
You should however not be doing this at all.
Instead, declare an appropriate ItemTemplate:
<ListBox ...>
<ListBox.ItemTemplate>
<DataTemplate>
<local:TimeAccountItem/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Then remove the explicit DataContext assignment from the UserControl
public partial class TimeAccountItem : UserControl
{
public TimeAccountItem()
{
InitializeComponent();
}
}
and assign a collection of data items instead of UI elements to the ListBox's ItemsSource property:
listbox.ItemSource = timeAccountList;

Binding Command in DataTemplate

I made UWP app with MVVM pattern(i think so :) )
My app have data template for listbox.
ListBox in View.xaml:
<ListBox x:Name="lbMySongs" ItemsSource="{Binding Path=MySongs}" ItemTemplate="{StaticResource lbSongsTemplate}" Grid.Row="1">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox/>
DateTemplate resource:
<DataTemplate x:Key="lbSongsTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=Artist}" Grid.Column="0"/>
<TextBlock Text=" - " Grid.Column="1"/>
<TextBlock Text="{Binding Path=Title}" Grid.Column="2"/>
//Here i bind command, but it's not binding cos ItemsSource have no command. ViewModel have this command.
<Button Command="{Binding Path=DownloadCommand}" Content="{Binding Path=Query}"/>
<TextBlock Text="{Binding Duration}" Grid.Column="5" HorizontalAlignment="Right"/>
</Grid>
</DataTemplate>
Need to set DataContext for this button.
My command in ViewModel. Example:
class ViewModel
{
...
public RelayCommand DownloadCommand { get; set; }
public ObservableCollection<SomeClass> MySongs { get; set; }
...
}
My problem: DataTemplate have ItemsSource = ViewModel.MySongs. But "DownloadCommand" is in ViewModel.
What i type in DataTemplate for my button set binding to DownloadCommand?
Try to use:
<Button Command="{Binding Path=DataContext.DownloadCommand, ElementName=lbMySongs}"
CommandParameter="{Binding}"
Content="{Binding Path=Query}"/>
And change
public RelayCommand DownloadCommand { get; set; }
to
public RelayCommand<SomeClass> DownloadCommand { get; set; }
...
DownloadCommand = new RelayCommand<SomeClass>(DownloadExecute);
...
private voir DownloadExecute(SomeClass item)
{
...
}
Use a RelativeSource binding to say you want to bind to ListBox.DataContext.DownloadCommand
<Button Command="{Binding Path=DataContext.DownloadCommand,
RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}"
Content="{Binding Path=Query}"/>
Its often common practice like this to also bind the CommandParameter to the item to know what item the command was executed on :
<Button Command="{Binding Path=DataContext.DownloadCommand,
RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}"
CommandParameter="{Binding }"
Content="{Binding Path=Query}" />
You could also use an ElementName binding, but then you have to give your ListBox a Name to specify in your DataTemplate, and I don't personally like to hardcode ElementName in my DataTemplates. Makes it annoying to find and fix if you change something's name, or want to use the DataTemplate in another ListBox.

Property update withing of MVVM

I've created an example based on MVVM
Main window XAML:
<Window x:Class="LearnMVVM.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:LearnMVVM"
xmlns:System="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Window.Resources>
<ObjectDataProvider x:Key="operationTypeEnum" MethodName="GetValues" ObjectType="{x:Type System:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:OperationType"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<DataTemplate DataType="{x:Type local:SomeUserControlViewModel}">
<local:SomeUserControl />
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="25"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="25"/>
<RowDefinition Height="25"/>
<RowDefinition />
</Grid.RowDefinitions>
<TextBox Grid.Column="0" Grid.Row="0" Margin="2" Text="{Binding Path=A, Mode=TwoWay}"/>
<TextBlock Grid.Column="1" Grid.Row="0" Text="+" TextAlignment="Center" VerticalAlignment="Center" Height="16" Margin="0,4,0,5"/>
<TextBox Grid.Column="2" Grid.Row="0" Margin="2" Text="{Binding Path=B, Mode=TwoWay}"/>
<Button Grid.Column="3" Grid.Row="0" Margin="2" Content="Посчитать" Command="{Binding ClickCommand}"/>
<TextBox Grid.Column="4" Grid.Row="0" Margin="2" IsReadOnly="True" Text="{Binding Path=Summa, Mode=TwoWay}"/>
<ComboBox Grid.Column="2" Grid.Row="1" SelectedItem="{Binding Path=SomeUserControl.Operation, Mode=TwoWay}" ItemsSource="{Binding Source={StaticResource operationTypeEnum}}" />
<ContentControl Grid.Column="2" Grid.Row="2" BorderThickness="3" BorderBrush="Black" Content="{Binding Path=SomeUserControl}" />
</Grid>
</Window>
XAML of the SomeUserControl:
<UserControl x:Class="LearnMVVM.SomeUserControl"
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:learnMvvm="clr-namespace:LearnMVVM"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
<learnMvvm:SomeUserControlViewModel />
</UserControl.DataContext>
<DockPanel>
<TextBox DockPanel.Dock="Top" Margin="10" Text="{Binding Path=A, Mode=TwoWay, diag:PresentationTraceSources.TraceLevel=High}" />
<Label DockPanel.Dock="Top" Content="{Binding Path=Operation, diag:PresentationTraceSources.TraceLevel=High}" />
<TextBox DockPanel.Dock="Top" Margin="10" Text="{Binding Path=B, Mode=TwoWay, diag:PresentationTraceSources.TraceLevel=High}" />
<Button DockPanel.Dock="Top" Content="=" Margin="20" Command="{Binding CalculateOperationComamnd, Mode=TwoWay, diag:PresentationTraceSources.TraceLevel=High}" />
<Label DockPanel.Dock="Top" Margin="10" Content="{Binding Path=Result, diag:PresentationTraceSources.TraceLevel=High}" />
</DockPanel>
</UserControl>
ViewModel of the SomeCustomUserControl:
using System;
using System.ComponentModel;
using System.Windows.Input;
namespace LearnMVVM
{
public enum OperationType
{
Sum,
Sub,
Div,
Mul
}
public class SomeUserControlViewModel : INotifyPropertyChanged
{
public double A { get; set; }
public double B { get; set; }
//Команды
private ICommand calculateOperationCommand;
public ICommand CalculateOperationComamnd
{
get
{
return calculateOperationCommand;
}
set
{
if (calculateOperationCommand != value)
{
calculateOperationCommand = value;
OnPropertyChanged("CalculateOperationComamnd");
}
}
}
private OperationType operation;
public OperationType Operation
{
get
{
return operation;
}
set
{
if (operation != value)
{
operation = value;
switch (operation)
{
case OperationType.Sum:
CalculateOperationComamnd = new RelayCommand(arg => OperationSum());
break;
case OperationType.Sub:
CalculateOperationComamnd = new RelayCommand(arg => OperationSub());
break;
case OperationType.Div:
CalculateOperationComamnd = new RelayCommand(arg => OperationDiv());
break;
case OperationType.Mul:
CalculateOperationComamnd = new RelayCommand(arg => OperationMul());
break;
}
OnPropertyChanged("Operation");
}
}
}
private void OperationSum()
{
Result = A + B;
}
private void OperationSub()
{
Result = A - B;
}
private void OperationDiv()
{
Result = A/B;
}
private void OperationMul()
{
Result = A*B;
}
private double result;
public double Result
{
get { return result; }
set
{
if (Math.Abs(result - value) > 0.0001)
{
result = value;
OnPropertyChanged("Result");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Problem: the custom control does not change, when i'm changing selected item from the combo box and the "calculate button has no effect.
Actually all properties within of SomeCustomControlViewModel are updated as expected, but there is no effect in the main windows.
Have i missed something?
Operation is not a property of SomeUserControl. It is a property of SomeUserControl's viewmodel -- reachable as the control's DataContext. Try binding ComboBox.SelectedItem like so:
SelectedItem="{Binding Path=SomeUserControl.DataContext.Operation, Mode=TwoWay}"
The change is that I added DataContext to the path.
This is why you don't use viewmodels with custom controls, if you really want to use them as controls. You write a control class deriving from Control, and give it dependency properties. Operation should be a dependency property of a class derived from Control, not a notifying property on a viewmodel. Then you define UI for it by applying a ControlTemplate via a default Style.
What you've got here is really a child viewmodel. With that type of arrangement, ordinarily the parent viewmodel would provide an instance of the child viewmodel, and bind it to the child control itself. Then anybody who wanted to use a property of the child viewmodel would bind ChildVM.WhateverProperty.

Fill Stackpanel inside Usercontrol

I'm searching for hours now but could not find the correct way how to do that.
I build a UserControl "MyToolbarGroup" having a GroupText and an empty Stackpanel inside.
Now I want to use the control MyToolbarGroup on my other UserControl "MyUserControl" and create some Buttons inside the Stackpanel of the MyToolbarGroup control.
MyToolbarGroup-XAML
<UserControl x:Class="TestUi.MyToolbarGroup"
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"
mc:Ignorable="d" d:DesignWidth="153" d:DesignHeight="103">
<Grid>
<Border BorderBrush="Black" BorderThickness="2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Center" Margin="5,5,5,5" />
<Label Grid.Row="1" HorizontalContentAlignment="Center" Content="{Binding Path=GroupText}" Background="LightBlue" />
</Grid>
</Border>
</Grid>
</UserControl>
MyToolbarGroup-Code
public partial class MyToolbarGroup : UserControl
{
public static readonly DependencyProperty GroupTextProperty = DependencyProperty.Register("GroupText", typeof(string), typeof(MyToolbarGroup));
public String GroupText
{
get { return (String)GetValue(GroupTextProperty); }
set { SetValue(GroupTextProperty, value); }
}
public MyToolbarGroup()
{
InitializeComponent();
DataContext = this;
}
}
MyUserControl-XAML
<UserControl
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:TestUi" x:Class="TestUi.MyUserControl"
mc:Ignorable="d"
d:DesignHeight="224" d:DesignWidth="343">
<Grid>
<local:MyToolbarGroup HorizontalAlignment="Left" Margin="40,30,0,0" VerticalAlignment="Top" Height="148" Width="238" GroupText="Test-Group">
<!-- Something like that
<Stackpanel-Inside-MyToolbarGroup>
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Height="65" Width="65" Content="Button 1"/>
</Stackpanel-Inside-MyToolbarGroup>
-->
</local:MyToolbarGroup>
</Grid>
</UserControl>
Any ideas how to set some buttons inside the stackpanel?
Thanx for any help!
include a ContentPresenter in MyToolbarGroup template to accept user Content
<UserControl x:Class="TestUi.MyToolbarGroup"
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:testUi="clr-namespace:TestUi"
mc:Ignorable="d" d:DesignWidth="153" d:DesignHeight="103">
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<Grid>
<Border BorderBrush="Black" BorderThickness="2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<ContentPresenter Content="{TemplateBinding Content}"/>
<Label Grid.Row="1" HorizontalContentAlignment="Center"
Content="{Binding GroupText, RelativeSource={RelativeSource AncestorType=testUi:MyToolbarGroup}}"
Background="LightBlue" />
</Grid>
</Border>
</Grid>
</ControlTemplate>
</UserControl.Template>
</UserControl>
and then use it like this:
<testUi:MyToolbarGroup Grid.Row="2"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Height="148" Width="238" GroupText="Test-Group">
<!--any content, e.g. -->
<StackPanel Orientation="Horizontal">
<TextBlock Text="A" Margin="5"/>
<TextBlock Text="B" Margin="5"/>
</StackPanel>
</testUi:MyToolbarGroup>
result:
A stack panel doesn't have the functionality you want (if I understand you correctly). What you want is a dynamic collection of controls based on some data right?
What you need is an ItemsControl:
<ItemsControl ItemsSource="{Binding MyButtonDataCollection}" >
<ItemsControl.ItemTemplate >
<DataTemplate >
<Button Content="{Binding ButtonName}"
Command="{Binding ButtonClick}"
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
bound to a collection of data objects that have the properties and commands you want to bind to:
class MyButtonData : INotifyPropertyChanged
{
String ButtonName { get; set; } //notify property changed and all that
public ICommand ButtonClick
{
get;
internal set;
}
private bool CanExecuteButtonClick()
{
//can this execute?
return true;
}
private void CreateButtonClick()
{
ButtonClick = new RelayCommand(SaveExecute, CanExecuteSaveCommand);
}
public void ButtonClickExecute()
{
//do my logic for click
}
}
If you want layout inside MyToolbarGroup to be predefined, you need to replace StackPanel with ItemsControl having StackPanel as ItemsPanel.
Here's MyToolbarGroup XAML:
<UserControl x:Class="WpfApplication2.MyToolbarGroup"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2">
<Border BorderBrush="Black" BorderThickness="2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<ItemsControl Grid.Row="0" HorizontalAlignment="Center" Margin="5,5,5,5"
ItemsSource="{Binding GroupItems, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<Label Grid.Row="1" HorizontalContentAlignment="Center"
Content="{Binding GroupText, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
Background="LightBlue" />
</Grid>
</Border>
</UserControl>
and code-behind:
public partial class MyToolbarGroup : UserControl
{
public MyToolbarGroup()
{
InitializeComponent();
}
public string GroupText
{
get { return (string)GetValue(GroupTextProperty); }
set { SetValue(GroupTextProperty, value); }
}
public static readonly DependencyProperty GroupTextProperty =
DependencyProperty.Register("GroupText", typeof(string), typeof(MyToolbarGroup), new PropertyMetadata(null));
public IEnumerable GroupItems
{
get { return (IEnumerable)GetValue(GroupItemsProperty); }
set { SetValue(GroupItemsProperty, value); }
}
public static readonly DependencyProperty GroupItemsProperty =
DependencyProperty.Register("GroupItems", typeof(IEnumerable), typeof(MyToolbarGroup), new PropertyMetadata(null));
}
Now, you can use it like this:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:WpfApplication2"
Title="MainWindow">
<Grid>
<local:MyToolbarGroup HorizontalAlignment="Left" Margin="40,30,0,0" VerticalAlignment="Top" Height="148" Width="238" GroupText="Test-Group">
<local:MyToolbarGroup.GroupItems>
<x:Array Type="{x:Type sys:Object}">
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Height="65" Width="65" Content="Button 1"/>
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Height="65" Width="65" Content="Button 2"/>
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Height="65" Width="65" Content="Button 3"/>
</x:Array>
</local:MyToolbarGroup.GroupItems>
</local:MyToolbarGroup>
</Grid>
</Window>
Screenshot of result:

WPF Databinding error on custom control

I am currently getting the following error
System.Windows.Data Error: 40 : BindingExpression path error: 'EthernetView' property not found on 'object' ''LabelTextBox' (Name='Root')'. BindingExpression:Path=EthernetView.SessionName; DataItem='LabelTextBox' (Name='Root'); target element is 'LabelTextBox' (Name='Root'); target property is 'Text' (type 'String')
when I bind data to a custom control like
<controls:LabelTextBox Grid.Column="0" Padding="10,5,10,0" LabelText="Name" Text="{Binding EthernetView.SessionName}"
TextWidth="175" IsReadOnly="True" IsError="False" HorizontalAlignment="Right"/>
I am pretty sure I have my Dependency property on the object set up correctly
public partial class LabelTextBox : UserControl, INotifyPropertyChanged
{
public string Text
{
get { return LblTextBox.Text; }
set
{
SetValue(TextProperty, value);
LblTextBox.Text = value;
PropertyChanged(this, new PropertyChangedEventArgs("Text"));
}
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(
"Text",
typeof(string),
typeof(LabelTextBox),
new PropertyMetadata(default(string), OnTextPropertyChanged));
private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
LabelTextBox source = d as LabelTextBox;
source.LblTextBox.Text = e.NewValue as string;
}
}
The xaml for the custom control looks like
<UserControl x:Class="Project.LabelTextBox"
x:Name="Root"
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"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d"
d:DesignHeight="20" d:DesignWidth="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition x:Name="TextWidthColumn" Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="LeftLabel" Grid.Column="0" Foreground="Black" Padding="0,0,10,0" VerticalAlignment="Center" FontSize="12"
Text="{Binding LabelText, ElementName=Root, Mode=TwoWay}"/>
<Border x:Name="TextBoxBorder" Grid.Column="1" BorderThickness="2" Background="#FAFAFAFA">
<TextBox x:Name="LblTextBox" VerticalAlignment="Center" Margin="1" FontSize="12" Text="{Binding Text}"
TextChanged="LblTextBox_TextChanged" KeyDown="OnKeyDownHandler"/>
</Border>
<Image x:Name="ErrorImage" Grid.Column="2" Source="pack://application:,,,/Project;component/Images/RedX.ico" Height="12" Width="12"/>
<TextBlock x:Name="RightLabel" Grid.Column="3" Foreground="Black" Padding="10,0,0,0" VerticalAlignment="Center" FontSize="12"
Text="{Binding LabelText, ElementName=Root, Mode=TwoWay}"/>
</Grid>
</UserControl>
The code works properly at runtime, but I would like to clean up the output errors. Can anyone give me some insight on how to fix this error.
Dependency Properties looks like this (GetValue, SetValue!):
public readonly static DependencyProperty IsBusyProperty = DependencyProperty.Register(
"IsBusy", typeof(bool), typeof(BusyControl), new PropertyMetadata(false));
public bool IsBusy
{
get { return (bool)GetValue(IsBusyProperty); }
set { SetValue(IsBusyProperty, value); }
}
so yours is not right.

Categories