User control with custom properties - c#

I'm trying to create a UserControl which is a legend of a graph item,
I've defined it so it has a label with the graph name, a check box which
define whether we show it or not and a rectangle with the graph color.
The xaml is defined like this:
<UserControl x:Class="Resources.LegendItem"
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">
<UserControl.Template>
<ControlTemplate>
<StackPanel Margin="1,0,0,0" Orientation="Horizontal">
<CheckBox Name="cb" IsChecked="{TemplateBinding IsGraphVisible}" >
<StackPanel Margin="1,0,0,0" Orientation="Horizontal">
<Rectangle Name="rec" RadiusX="2" RadiusY="2" Height="10" Width="10" />
<Label Name="lab" Content="{TemplateBinding GraphName}" />
</StackPanel>
</CheckBox>
</StackPanel>
</ControlTemplate>
</UserControl.Template>
</UserControl>
and the cs file is:
namespace Resources
{
public partial class LegendItem : UserControl
{
public static readonly DependencyProperty IsGraphVisibleProperty = DependencyProperty.Register("IsGraphVisible", typeof(Boolean), typeof(LegendItem));
public static readonly DependencyProperty GraphNameProperty = DependencyProperty.Register("GraphName", typeof(String), typeof(LegendItem));
public bool IsGraphVisible
{
get { return (bool)GetValue(IsGraphVisibleProperty); }
set { SetValue(IsGraphVisibleProperty, value); }
}
public string GraphName
{
get { return (string)GetValue(GraphNameProperty); }
set { SetValue(GraphNameProperty, value); }
}
public LegendItem()
{
InitializeComponent();
}
}
}
But when I compile it, I get an error "Cannot find the static member 'IsGraphVisibleProperty' on the type 'Control'."
Any help would be appreciated.

You do not need a template at all. UserControl allows the XAML to be declared directly. You can't set the template in a UserControl:
<UserControl x:Class="Resources.LegendItem" x:Name="MyControl"
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">
<StackPanel Margin="1,0,0,0" Orientation="Horizontal">
<CheckBox Name="cb" IsChecked="{Binding ElementName=MyControl, Path=IsGraphVisible}" >
<StackPanel Margin="1,0,0,0" Orientation="Horizontal">
<Rectangle Name="rec" RadiusX="2" RadiusY="2" Height="10" Width="10" />
<Label Name="lab" Content="{Binding ElementName=MyControl, Path=GraphName}" />
</StackPanel>
</CheckBox>
</StackPanel>
</UserControl>

You need to specify TargetType="{x:Type Resources.LegendItem}" on your ControlTemplate, otherwise it defaults to being a template for Control and you get that error.

Related

wpf xaml MVVM inheritance with multiple ContentPresenter

I am rewriting import masks that have a lot in common, so I want (and must) use inheritance.
I have a basic UserControl with all common controls: (I have left out the grid definitions)
BaseClass.xaml
<UserControl x:Class="BaseImport.BaseClass"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<Grid>
<Border Grid.Row="0" Grid.Column="0">
<StackPanel>
<Label Content="Text1:"/>
<ComboBox Name="cbText1" MinWidth="80"/>
</StackPanel>
</Border>
<Border Grid.Row="0" Grid.Column="1">
<StackPanel>
<Label Content="Text2:"/>
<ComboBox Name="cbText2" MinWidth="80"/>
</StackPanel>
</Border>
<Border Grid.Row="0" Grid.Column="2">
<StackPanel>
<ContentPresenter ContentSource="Content"/> <!-- ContentSource="Content" is the default-->
</StackPanel>
</Border>
<!-- next Row -->
<Border Grid.Row="1" Grid.Column="0">
<StackPanel>
<Label Content="Text3:"/>
<TextBox Name="tbText3" TextWrapping="Wrap" Text="" MinWidth="80" VerticalAlignment="Center"/>
</StackPanel>
</Border>
<Border Grid.Row="1" Grid.Column="1">
<StackPanel>
<ContentPresenter/>
</StackPanel>
</Border>
</Grid>
</ControlTemplate>
</UserControl.Template>
</UserControl>
This is a kind of Template that gets "used" like this:
MainWindow.xaml (just for demonstration a mainwindow)
<Window x:Class="zzz.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:my="clr-namespace:BaseImport;assembly=BaseImport"
mc:Ignorable="d"
Title="MainWindow" Height="280" Width="600">
<my:BaseClass>
<StackPanel>
<Label Content="Test:"/>
<ComboBox ItemsSource="{Binding TestTyps}" MinWidth="80"/>
</StackPanel>
</my:BaseClass>
</Window>
MainWindow.xaml.cs
using WpfApp1.ViewModel;
namespace zzz
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
}
}
and to wrap it up MainViewModel.cs:
namespace WpfApp1.ViewModel
{
public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
public string[] TestTyps { get { return new string[] { "abc", "123", "xyz" }; } }
}
}
If I have one ContentPresenter everything works fine. But in the BaseClass I have two, potentially more.
Like this only the "last" Presenter gets populated. And in MainWindow.xaml can only be one declared.
How can I put more Content in MainWindow.xaml?
How can I select the right one?
Thanks
The red rectangle is were the second presenter is located (row 1, column 1) but I want it to be were the arrow points (row 0, column 2).
I want another control in place of the red rectangle also declared in MainWindow.xaml.
The stuff you put directly in the <my:BaseClass> tags is the contentProperty which can be only one.
Why only the last ContentPresenter shows it? Because each VisualElement can only have one parent, so the last one claiming it wins.
However you can create as many properties as you want.
<UserControl x:Class="BaseImport.BaseClass"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<Grid>
<Border Grid.Row="0" Grid.Column="0">
<StackPanel>
<Label Content="Text1:"/>
<ComboBox Name="cbText1" MinWidth="80"/>
</StackPanel>
</Border>
<Border Grid.Row="0" Grid.Column="1">
<StackPanel>
<Label Content="Text2:"/>
<ComboBox Name="cbText2" MinWidth="80"/>
</StackPanel>
</Border>
<Border Grid.Row="0" Grid.Column="2">
<StackPanel>
<ContentPresenter ContentSource="FirstContent"/>
</StackPanel>
</Border>
<!-- next Row -->
<Border Grid.Row="1" Grid.Column="0">
<StackPanel>
<Label Content="Text3:"/>
<TextBox Name="tbText3" TextWrapping="Wrap" Text="" MinWidth="80" VerticalAlignment="Center"/>
</StackPanel>
</Border>
<Border Grid.Row="1" Grid.Column="1">
<StackPanel>
<ContentPresenter ContentSource="SecondContent"/>
</StackPanel>
</Border>
</Grid>
</ControlTemplate>
</UserControl.Template>
</UserControl>
<my:BaseClass>
<my:BaseClass.FirstContent>
<StackPanel>
<Label Content="Test:"/>
<ComboBox ItemsSource="{Binding TestTyps}" MinWidth="80"/>
</StackPanel>
<my:BaseClass.FirstContent>
<my:BaseClass.SecondContent>
<StackPanel>
<Label Content="Test10:"/>
<TextBox Text="{Binding Whatever}" MinWidth="80"/>
</StackPanel>
<my:BaseClass.SecondContent>
</my:BaseClass>
I gave the point to Firo, because he gave me the right answer. But I want to give some final thoughts, so everyone can benefit from it.
For the xaml "base-class":
<UserControl
x:Class="BaseImport.BaseClass"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<Grid>
<Border Grid.Row="0" Grid.Column="0">
<StackPanel>
<Label Content="Text1:"/>
<ComboBox Name="cbText1" MinWidth="80"/>
</StackPanel>
</Border>
<Border Grid.Row="0" Grid.Column="1">
<StackPanel>
<Label Content="DB:"/>
<ComboBox
Name="cbxDB"
ItemsSource="{Binding DBs}"
SelectedItem="{Binding DB}"
MinWidth="80"
SelectionChanged="selectionChanged"
Loaded="DB_Loaded">
<!--
here the DataContext can be set differently
<ComboBox.DataContext>
<Views:DBViewModel/>
</ComboBox.DataContext>
-->
</ComboBox>
</StackPanel>
</Border>
<!-- the content placeholder -->
<Border Grid.Row="0" Grid.Column="2">
<StackPanel MinWidth="80">
<ContentControl
Content="{Binding ContentOne}"
ContentTemplate="{Binding ContentOneTemplate}"
ContentTemplateSelector="{Binding ContentOneTemplateSelector}"/>
</StackPanel>
</Border>
<Border Grid.Row="0" Grid.Column="3">
<StackPanel MinWidth="80">
<ContentControl
Content="{Binding ContentTwo}"
ContentTemplate="{Binding ContentTwoTemplate}"
ContentTemplateSelector="{Binding ContentTwoTemplateSelector}"/>
</StackPanel>
</Border>
</Grid>
</ControlTemplate>
</UserControl.Template>
</UserControl>
For the "base-class":
public partial class BaseClass : UserControl
{
public BaseClass(BaseViewModel _bvm)
{
InitializeComponent();
// set DataContext for all other controls
DataContext = _bvm;
}
#region DependencyProperty
public static readonly DependencyProperty ContentOneProperty = DependencyProperty.Register("ContentOne", typeof(object), typeof(BaseImportClass));
public static readonly DependencyProperty ContentOneTemplateProperty = DependencyProperty.Register("ContentOneTemplate", typeof(DataTemplate), typeof(BaseImportClass));
public static readonly DependencyProperty ContentOneTemplateSelectorProperty = DependencyProperty.Register("ContentOneTemplateSelector", typeof(DataTemplateSelector), typeof(BaseImportClass));
public static readonly DependencyProperty ContentTwoProperty = DependencyProperty.Register("ContentTwo", typeof(object), typeof(BaseImportClass));
public static readonly DependencyProperty ContentTwoTemplateProperty = DependencyProperty.Register("ContentTwoTemplate", typeof(DataTemplate), typeof(BaseImportClass));
public static readonly DependencyProperty ContentTwoTemplateSelectorProperty = DependencyProperty.Register("ContentTwoTemplateSelector", typeof(DataTemplateSelector), typeof(BaseImportClass));
public object ContentOne
{
get { return GetValue(ContentOneProperty); }
set { SetValue(ContentOneProperty, value); }
}
public DataTemplate ContentOneTemplate
{
get { return (DataTemplate)GetValue(ContentOneTemplateProperty); }
set { SetValue(ContentOneTemplateProperty, value); }
}
public DataTemplateSelector ContentOneTemplateSelector
{
get { return (DataTemplateSelector)GetValue(ContentOneTemplateSelectorProperty); }
set { SetValue(ContentOneTemplateSelectorProperty, value); }
}
public object ContentTwo
{
get { return GetValue(ContentTwoProperty); }
set { SetValue(ContentTwoProperty, value); }
}
public DataTemplate ContentTwoTemplate
{
get { return (DataTemplate)GetValue(ContentTwoTemplateProperty); }
set { SetValue(ContentTwoTemplateProperty, value); }
}
public DataTemplateSelector ContentTwoTemplateSelector
{
get { return (DataTemplateSelector)GetValue(ContentTwoTemplateSelectorProperty); }
set { SetValue(ContentTwoTemplateSelectorProperty, value); }
}
#endregion
}
For the "derived" class xaml:
<UserControl x:Class="WpfApp1.DerivedClass"
xmlns:base="clr-namespace:BaseImport.Views;assembly=BaseImport"
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">
<StackPanel>
<base:BaseClass Name="bc">
<base:BaseClass.ContentOneTemplate>
<DataTemplate>
<StackPanel>
<Label Content="Test:"/>
<ComboBox
ItemsSource="{Binding TestTyps}"
SelectedItem="{Binding TestTyp}"
SelectionChanged="TestTypChanged"
Loaded="TestTypLoaded">
<!--
here the DataContext can be set differently
<ComboBox.DataContext>
<Views:ViewModelXYZ/>
</ComboBox.DataContext>
-->
</ComboBox>
</StackPanel>
</DataTemplate>
</base:BaseClass.ContentOneTemplate>
</base:BaseClass>
</StackPanel>
</UserControl>
For the "derived" class:
public partial class DerivedClass : UserControl
{
ViewModelABC vmabc = null;
public DerivedClass(ViewModel _vm) : this()
{
vmabc = _vm;
this.DataContext = vmabc;
bc.DataContext = vmabc; // or another viewmodel that holds TestTyps and TestTyp or leave it for ViewModelXYZ
}
private void TestTypChanged(object sender, SelectionChangedEventArgs e)
{
PropertyChanged();
}
private void TestTypLoaded(object sender, RoutedEventArgs e)
{
(sender as ComboBox).SelectedValue = (this.DataContext as ViewModel(XYZ/ABC).TestTyp;
}
}
That is what I came up.
I hope that helps others.

Setting a Button Image to Binding in its Style

I am finding that I am creating the same Button style for multiple buttons but only changing one part - the image that is used on the Button. An example;
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="pack://application:,,,/Resources/MainWindowIcons/Staff.ico" Height="20"/>
<TextBlock Style="{StaticResource HoverUnderlineStyle}" Text="Staff" Margin="5,0,0,0"/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
This is the code for the staff Button. If I wanted to add another button I'd replicate the whole style but just change the Source of the Image.
Is there a way I can have on style and then set this on the Button itself - meaning that I don't have to replicate the style multiple times?
You could implement two attached properties - one for the Image source and one for the text - that you can set on any Button:
public class ButtonProperties
{
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.RegisterAttached("ImageSource", typeof(Uri), typeof(ButtonProperties));
public static Uri GetImageSource(Button button)
{
return (Uri)button.GetValue(ImageSourceProperty);
}
public static void SetImageSource(Button button, Uri value)
{
button.SetValue(ImageSourceProperty, value);
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.RegisterAttached("Text", typeof(Uri), typeof(ButtonProperties));
public static string GetText(Button button)
{
return (string)button.GetValue(ImageSourceProperty);
}
public static void SetText(Button button, string value)
{
button.SetValue(ImageSourceProperty, value);
}
}
Then you only need to define the ContentTemplate once as a resource, for example in your App.xaml:
<Application x:Class="WpfApplication1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
StartupUri="MainWindow.xaml">
<Application.Resources>
<DataTemplate x:Key="dataTemplate">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Path=(local:ButtonProperties.ImageSource), RelativeSource={RelativeSource AncestorType=Button}}" Height="20"/>
<TextBlock Text="{Binding Path=(local:ButtonProperties.Text), RelativeSource={RelativeSource AncestorType=Button}}" Margin="5,0,0,0"/>
</StackPanel>
</DataTemplate>
</Application.Resources>
</Application>
Usage:
<Button local:ButtonProperties.Text="Staff"
local:ButtonProperties.ImageSource="pack://application:,,,/Resources/MainWindowIcons/Staff.ico"
ContentTemplate="{StaticResource dataTemplate}" />

Simple popup dialog in WPF (overlay inside Window)

I'm working on a modal dialog popup (I'm not sure about the exact UX term) that is displayed inline, inside of a control or window with darkened background.
Visual example
What I tried is putting a <ContentPresenter /> inside the XAML of the popup and then just instantiate it like this:
<local:Popup Grid.RowSpan="2">
<TextBlock Text="Popup test..." />
</local:Popup>
However, the XAML replaces the entire Popup XAML instead of being placed where the ContentPresenter is.
Q: How is the ContentPresenter here used properly?
Popup.xaml
<ContentControl
x:Class="[...].Popup"
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:[...]"
mc:Ignorable="d" d:DesignWidth="300" d:DesignHeight="300">
<Grid Background="#7f000000">
<Grid Background="White" HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel Margin="20">
<TextBlock Text="{Binding Title, RelativeSource={RelativeSource AncestorType=UserControl}}" FontSize="20" />
<ContentPresenter />
</StackPanel>
</Grid>
</Grid>
</ContentControl>
Popup.xaml.cs
using System.Windows;
namespace [...]
{
public partial class Popup : ContentControlBase
{
public static DependencyProperty TitleProperty = DependencyProperty.Register(nameof(Title), typeof(string), typeof(Popup));
public string Title
{
get
{
return (string)GetValue(TitleProperty);
}
set
{
SetValue(TitleProperty, value);
}
}
public Popup()
{
InitializeComponent();
}
}
}
The content of your Popup should be defined as a ControlTemplate for the ContentPresenter to work as expected here. Please refer to the following sample code.
Popup.xaml:
<ContentControl
x:Class="WpfApplication1.Popup"
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:WpfApplication1"
mc:Ignorable="d" d:DesignWidth="300" d:DesignHeight="300"
x:Name="popup">
<ContentControl.Template>
<ControlTemplate TargetType="local:Popup">
<Grid Background="#7f000000">
<Grid Background="White" HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel Margin="20">
<TextBlock Text="{Binding Title, ElementName=popup}" FontSize="20" />
<ContentPresenter />
</StackPanel>
</Grid>
</Grid>
</ControlTemplate>
</ContentControl.Template>
Popup1.xaml.cs.
public partial class Popup : ContentControl
{
public static DependencyProperty TitleProperty = DependencyProperty.Register(nameof(Title), typeof(string), typeof(Popup));
public string Title
{
get
{
return (string)GetValue(TitleProperty);
}
set
{
SetValue(TitleProperty, value);
}
}
public Popup()
{
InitializeComponent();
}
}
}
Window1.xaml:
<local:Popup Title="Title...">
<TextBlock>Text...</TextBlock>
</local:Popup>

WPF CustomControl stops showing up 50% of the time

I have a custom control in my WPF project, I've defined the style, the class, and I've called it in my xaml and made sure to include a reference. when I start the project 50% of the time the custom object renders perfectly, the other 50% it just doesn't show up, but is still interactable.
public class PermissionBox : Control
{
public PermissionBox()
{
DefaultStyleKey = typeof(PermissionBox);
}
public string PumpID
{
get
{
return (string)GetValue(PumpIDProperty);
}
set
{
SetValue(PumpIDProperty, value);
}
}
public static readonly DependencyProperty PumpIDProperty =
DependencyProperty.Register("PumpID", typeof(string), typeof(PermissionBox), new PropertyMetadata(string.Empty));
public string FuelType
{
get
{
return (string)GetValue(FuelTypeProperty);
}
set
{
SetValue(FuelTypeProperty, value);
}
}
public static readonly DependencyProperty FuelTypeProperty =
DependencyProperty.Register("FuelType", typeof(string), typeof(PermissionBox), new PropertyMetadata(string.Empty));
}
in Themes/Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:POS">
<Style TargetType="{x:Type controls:PermissionBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:PermissionBox}">
<Grid Background="Gray" Opacity="0.8">
<TextBlock Foreground="Black" FontSize="15" Text="Pump ID:" HorizontalAlignment="Left"/>
<TextBlock Foreground="Black" FontSize="15" Text="{TemplateBinding PumpID}" HorizontalAlignment="Right"/>
<TextBlock Foreground="Black" FontSize="15" Text="Fuel Type:" HorizontalAlignment="Left"/>
<TextBlock Foreground="Black" FontSize="15" Text="{TemplateBinding FuelType}" HorizontalAlignment="Right"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and the calling window
<Window x:Class="POS.Window1"
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:controls="clr-namespace:POS"
mc:Ignorable="d"
Title="Window1" Height="300" Width="300">
<Grid>
<controls:PermissionBox PumpID="100" FuelType="Unleaded"> </controls:PermissionBox>
</Grid>
</Window>
Really appreciate any help with resolving this.

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:

Categories