C#WPF自定义用户控件 [英] C# WPF custom user control
问题描述
你好
我需要使用自定义用户控件。
我使用这个XAML代码:
Hello
I need to use a custom user control.
I use this XAML code :
<UserControl x:Class="WpfClickableImage.ControlClickableImage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="UC"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
>
<StackPanel>
<Button
Click="Button_Click"
Margin="10"
HorizontalAlignment="Center"
ToolTip="Click on Fred">
<Button.Template>
<ControlTemplate>
<Border x:Name="theBorder"
BorderBrush="Transparent"
BorderThickness="2">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image
Grid.Column="0"
Source="{Binding ElementName=UC, Path=Image}"
Width="{Binding ElementName=UC, Path=ImageWidth}"
Height="{Binding ElementName=UC, Path=ImageHeight}"/>
<StackPanel
Grid.Column="1"
Orientation="Vertical" Margin="5">
<TextBlock
Text="{Binding ElementName=UC, Path=Text1}"
Margin="10,0,0,0"/>
<TextBlock
Text="{Binding ElementName=UC, Path=Text2}"
Margin="10,0,0,0"/>
</StackPanel>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" TargetName="theBorder"
Value="LightSkyBlue"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
</StackPanel>
</UserControl>
CS背后:
And behind CS :
namespace WpfClickableImage
{
/// <summary>
/// Logique d'interaction pour ControlClickableImage.xaml
/// </summary>
public partial class ControlClickableImage : UserControl
{
public ControlClickableImage()
{
InitializeComponent();
}
public ImageSource Image
{
get { return (ImageSource)GetValue(ImageProperty); }
set { SetValue(ImageProperty, value); }
}
// Using a DependencyProperty as the backing store for Image. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ImageProperty =
DependencyProperty.Register("Image", typeof(ImageSource), typeof(ControlClickableImage),
new UIPropertyMetadata(null));
public ImageSource ImageBack
{
get { return (ImageSource)GetValue(ImageBackProperty); }
set { SetValue(ImageBackProperty, value); }
}
// Using a DependencyProperty as the backing store for ImageBack. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ImageBackProperty =
DependencyProperty.Register("ImageBack", typeof(ImageSource), typeof(ControlClickableImage),
new UIPropertyMetadata(null));
public double ImageWidth
{
get { return (double)GetValue(ImageWidthProperty); }
set { SetValue(ImageWidthProperty, value); }
}
// Using a DependencyProperty as the backing store for ImageWidth. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ImageWidthProperty =
DependencyProperty.Register("ImageWidth", typeof(double), typeof(ControlClickableImage),
new UIPropertyMetadata(16d));
public double ImageHeight
{
get { return (double)GetValue(ImageHeightProperty); }
set { SetValue(ImageHeightProperty, value); }
}
// Using a DependencyProperty as the backing store for ImageHeight. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ImageHeightProperty =
DependencyProperty.Register("ImageHeight", typeof(double), typeof(ControlClickableImage),
new UIPropertyMetadata(16d));
public string Text1
{
get { return (string)GetValue(Text1Property); }
set { SetValue(Text1Property, value); }
}
// Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc...
public static readonly DependencyProperty Text1Property =
DependencyProperty.Register("Text1", typeof(string), typeof(ControlClickableImage),
new UIPropertyMetadata(""));
public string Text2
{
get { return (string)GetValue(Text2Property); }
set { SetValue(Text2Property, value); }
}
// Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc...
public static readonly DependencyProperty Text2Property =
DependencyProperty.Register("Text2", typeof(string), typeof(ControlClickableImage),
new UIPropertyMetadata(""));
public bool IsChecked
{
get { return (bool)GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, value); }
}
// Using a DependencyProperty as the backing store for IsChecked. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register("IsChecked", typeof(bool), typeof(ControlClickableImage),
new UIPropertyMetadata(false));
private void Button_Click(object sender, RoutedEventArgs e)
{
this.IsChecked = !IsChecked;
// swap images
ImageSource temp = this.Image;
this.Image = this.ImageBack;
this.ImageBack = temp;
//les 2 events sont declenches ici
if (this.IsChecked)
OnCheckedChanged(this, e);
else if (!this.IsChecked )
OnUnCheckChanged(this, e);
}
// event perso Checked
public static readonly RoutedEvent CheckedEvent = EventManager.RegisterRoutedEvent(
"Checked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ControlClickableImage));
// Provide CLR accessors for the event
public event RoutedEventHandler Checked
{
add { AddHandler(CheckedEvent, value); }
remove { RemoveHandler(CheckedEvent, value); }
}
// event perso UnChecked
public static readonly RoutedEvent UnCheckedEvent = EventManager.RegisterRoutedEvent(
"UnChecked", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ControlClickableImage));
// Provide CLR accessors for the event
public event RoutedEventHandler UnChecked
{
add { AddHandler(UnCheckedEvent, value); }
remove { RemoveHandler(UnCheckedEvent, value); }
}
// methods charge de raiser les 2 events
private void OnCheckedChanged(ControlClickableImage controlClickableImage, RoutedEventArgs e)
{
RoutedEventArgs newEventArgs = new RoutedEventArgs( ControlClickableImage.CheckedEvent);
newEventArgs.Source = controlClickableImage;
RaiseEvent(newEventArgs);
}
private void OnUnCheckChanged(ControlClickableImage controlClickableImage, RoutedEventArgs e)
{
RoutedEventArgs newEventArgs = new RoutedEventArgs(ControlClickableImage.UnCheckedEvent);
newEventArgs.Source = controlClickableImage;
RaiseEvent(newEventArgs);
}
}
}
In my application :
<Window x:Class="WpfClickableImage.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:WpfClickableImage"
Title="Window1" Height="300" Width="300">
<StackPanel>
<my:ControlClickableImage
x:Name="yourcontrol"
Image="calendar.png"
ImageBack="worldwallpaper.jpg"
Text1="ControlImageClickable"
Text2="Description"
HorizontalAlignment="Left" VerticalAlignment="Top"
ImageWidth="20" ImageHeight="20" Margin="10"
Checked="ControlClickableImage_Checked"
UnChecked="ControlClickableImage_UnChecked">
</my:ControlClickableImage>
</StackPanel>
</Window>
和CS背后我的申请:
And Behind CS on my application :
namespace WpfClickableImage
{
/// <summary>
/// Logique d'interaction pour Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void ControlClickableImage_Checked(object sender, RoutedEventArgs e)
{
ControlClickableImage ctl = e.Source as ControlClickableImage;
MessageBox.Show(ctl.IsChecked.ToString() + " ; " + ctl.Text1 + " ; " + ctl.Text2);
}
private void ControlClickableImage_UnChecked(object sender, RoutedEventArgs e)
{
ControlClickableImage ctl = e.Source as ControlClickableImage;
MessageBox.Show(ctl.IsChecked.ToString() + " ; " + ctl.Text1 + " ; " + ctl.Text2);
}
}
}
这是工作。
但是,之前,在我的复选框上,我发送此绑定:
It's work.
But, before, on my checkbox, i send this Binding :
IsChecked="{Binding bChecked}"
< br $> b $ b
要检查所有例子:
To check all for example :
private void btnTous_Click(object sender, RoutedEventArgs e)
{
foreach (var item in LstIlots)
{
item.bChecked = true;
}
}
但是这个属性IsChecked在我的用户控件中不起作用。
如何启用此参数?
谢谢。
我尝试过:
我尝试添加此IsChecked Binding bChecked in my XAML Application code:
But this property IsChecked don't work in my user control.
How i can enable this parameter ?
Thank you.
What I have tried:
I try to add this IsChecked Binding bChecked in my XAML Application code :
<my:ControlClickableImage
x:Name="yourcontrol"
Image="calendar.png"
ImageBack="worldwallpaper.jpg"
Text1="ControlImageClickable"
Text2="Description"
HorizontalAlignment="Left" VerticalAlignment="Top"
ImageWidth="20" ImageHeight="20" Margin="10"
Checked="ControlClickableImage_Checked"
UnChecked="ControlClickableImage_UnChecked"
IsChecked="{Binding bChecked}>
</my:ControlClickableImage>
但它不是工作。
提前谢谢。
But it's not work.
Thank you in advance.
推荐答案
史蒂夫,
我回到了承诺的例子:它显示了3种不同的方法(这里可能有第4种行为
,但我不知道我想混淆你;)
我建议把它放在一个新的WPF应用程序Project中让它运行:
首先你需要一个CustomControl:
Hi Steve,
I'm back with the promised example: It Shows 3 different approaches (a 4th would be possible here withbehaviors
, but I don't want to confuse you ;)
I suggest put this in a new WPF application Project and let it run:
first you need a CustomControl:
public class CustomControl : Control
{
static CustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl), new FrameworkPropertyMetadata(typeof(CustomControl)));
}
public bool IsChecked
{
get { return (bool)GetValue(IsCheckedProperty); }
set
{
SetValue(IsCheckedProperty, value);
}
}
// Using a DependencyProperty as the backing store for IsChecked. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register("IsChecked", typeof(bool), typeof(CustomControl), new PropertyMetadata(false));
}
和generic.xaml(该文件应在名为Themes的文件夹中生成如果你将一个自定义控件添加到WPF项目中。
and in generic.xaml (the file should be generated inside a Folder named "Themes" if you add a customcontrol to a WPF Project).
esourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp4">
<Style TargetType="{x:Type local:CustomControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<StackPanel>
<TextBlock> UserControl - a composition of existing Controls</TextBlock>
<CheckBox Content="I represent the checked state" IsChecked="{TemplateBinding IsChecked}"></CheckBox>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
秒一个UserControl:
second a UserControl:
<UserControl x:Class="WpfApp4.UserControl1"
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:WpfApp4"
mc:Ignorable="d">
<StackPanel Background="White" Width="301">
<TextBlock> UserControl - a composition of existing Controls</TextBlock>
<CheckBox x:Name="checkbox" Content="I represent the checked state"></CheckBox>
</StackPanel>
</UserControl>
public partial class UserControl1 : UserControl
{
public bool IsChecked
{
get { return (bool)GetValue(IsCheckedProperty); }
set
{
SetValue(IsCheckedProperty, value);
checkbox.IsChecked = value;
}
}
// Using a DependencyProperty as the backing store for IsChecked. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register("IsChecked", typeof(bool), typeof(UserControl1), new PropertyMetadata(false));
public UserControl1()
{
InitializeComponent();
}
}
third a ViewModel
third a ViewModel
public class ViewModel : INotifyPropertyChanged
{
bool m_bIsChecked; // backing field for property IsChecked
public event PropertyChangedEventHandler PropertyChanged;
public bool IsChecked
{
get { return m_bIsChecked; }
set
{
if (m_bIsChecked != value)
{
m_bIsChecked = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsChecked))); // this is just for example, don't raise event directly from code, always create a dedicated OnXXX method to just raise the event
}
}
}
}
and a test-window (I used pre-generated MainWindow) to Show the use:
and a test-window (I used pre-generated MainWindow) to Show the use:
<Window x:Class="WpfApp4.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:local="clr-namespace:WpfApp4"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<local:UserControl1 x:Name="usercontrol1">
</local:UserControl1>
<local:CustomControl x:Name="customcontrol">
</local:CustomControl>
<CheckBox x:Name="checkbox" Content="I'm cooler - using MVVM and let the WPF do the rest" IsChecked="{Binding IsChecked}"></CheckBox>
<Button Content="Set checked state by code" Click="Button_Click"> </Button>
</StackPanel>
</Window>
with Code behind:
with Code behind:
public partial class MainWindow : Window
{
ViewModel m_viewmodel = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = m_viewmodel;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
usercontrol1.IsChecked = !usercontrol1.IsChecked;
customcontrol.IsChecked = !customcontrol.IsChecked;
checkbox.IsChecked = !checkbox.IsChecked;
}
}
So what we see here:
1. a UserControl is just a bunch of other Controls + some DependencyProperties - nothing is done with binding (because you don’t Control the DataContext)
2. a CustomControl with a generic theme and TemplateBinding - better for your case I’d say.
3. Don’t create any controls use a pattern to bind data - MVVM - we just set the ViewModel as DataContext for the window and let the binding-mechanism do the rest.
I would always go for approach 3 nowadays, sometimes you will still need approach 2, Approach 1 - as you found out for yourself is \"not so good\", because it’s not so easys to make it right without a lot of knowledge of WPF (same is true for Approach 2).
I hope this helps you to find your current Problem.
Kind regards
Johannes
So what we see here:
1. a UserControl is just a bunch of other Controls + some DependencyProperties - nothing is done with binding (because you don't Control the DataContext)
2. a CustomControl with a generic theme and TemplateBinding - better for your case I'd say.
3. Don't create any controls use a pattern to bind data - MVVM - we just set the ViewModel as DataContext for the window and let the binding-mechanism do the rest.
I would always go for approach 3 nowadays, sometimes you will still need approach 2, Approach 1 - as you found out for yourself is "not so good", because it's not so easys to make it right without a lot of knowledge of WPF (same is true for Approach 2).
I hope this helps you to find your current Problem.
Kind regards
Johannes
这篇关于C#WPF自定义用户控件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!