自定义 WPF 控件可以实现 IsDefault 属性吗
我有一个不是从 Button 派生的自定义按钮控件。我是否可以实现相当于 IsDefault 以便调用与我的控件关联的命令。我希望这是一个附加属性,我可以将其添加到任何控件中,但据我所知,似乎并非如此。如果我的控件不是从 Button 派生的,或者至少有一个合理的解决方法,我会运气不好吗?
更新: 我只是用 Reflector 看了一下 Button 下面是如何完成此操作的,我必须说这不是我见过的最不言自明的代码。看起来至少有 3 个依赖属性(一些自定义类型)只是为了处理默认 Button 的概念。由于似乎没有现有的方法来借用 IsDefault 功能,我想我必须缩小我想要实现的目标,以便我至少可以获得默认焦点并访问关键处理来工作并忽略Button.IsDefault 实现中涉及的复杂性。
更新: 添加了以下代码示例,显示了我尝试 itowlson 的建议的失败尝试。
MyButton.xaml
<UserControl x:Class="IsDefault.MyButton"
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"
Height="28"
Width="117">
<Grid>
<Button Click="Button_Click">
<Button.Template>
<ControlTemplate>
<Border BorderThickness="2"
CornerRadius="12"
Background="DarkSlateBlue">
<TextBlock Foreground="WhiteSmoke"
HorizontalAlignment="Center"
VerticalAlignment="Center">Some Text</TextBlock>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</UserControl>
MyButton.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace IsDefault
{
/// <summary>
/// Interaction logic for MyButton.xaml
/// </summary>
public partial class MyButton : UserControl
{
// Provide CLR accessors for the event
public event RoutedEventHandler Click
{
add { AddHandler(ClickEvent, value); }
remove { RemoveHandler(ClickEvent, value); }
}
// Using a RoutedEvent
public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent(
"Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyButton));
public bool IsDefault
{
get { return (bool)GetValue(IsDefaultProperty); }
set { SetValue(IsDefaultProperty, value); }
}
public static readonly DependencyProperty IsDefaultProperty =
DependencyProperty.Register(
"IsDefault",
typeof(bool),
typeof(MyButton),
new PropertyMetadata(false, IsDefault_PropertyChangedCallback, null));
public MyButton()
{
InitializeComponent();
}
protected override void OnAccessKey(AccessKeyEventArgs e)
{
base.OnAccessKey(e);
if (e.Key == "\r")
{
if (e.IsMultiple)
{
// There are multiple controls that are currently handling the Enter key
MessageBox.Show("there are multiple controls handling the Enter key.");
}
else
{
RaiseEvent(new RoutedEventArgs(ClickEvent, this));
}
}
}
private static void IsDefault_PropertyChangedCallback(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var button = d as MyButton;
var isDefault = (bool)e.NewValue;
if (isDefault)
{
AccessKeyManager.Register("\r", button);
}
else
{
AccessKeyManager.Unregister("\r", button);
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
RaiseEvent(new RoutedEventArgs(ClickEvent));
}
}
}
MainWindow.xaml
<Window x:Class="IsDefault.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:my="clr-namespace:IsDefault">
<Grid>
<Button Content="Button"
Height="23"
HorizontalAlignment="Left"
Margin="224,24,0,0"
Name="button1"
VerticalAlignment="Top"
Width="75" />
<TextBox Height="23"
HorizontalAlignment="Left"
Margin="208,94,0,0"
Name="textBox1"
VerticalAlignment="Top"
Width="120" />
<my:MyButton Height="28"
HorizontalAlignment="Left"
Margin="232,154,0,0"
x:Name="myButton1"
VerticalAlignment="Top"
Width="117"
Click="myButton1_Click"
IsDefault="True"/>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace IsDefault
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void myButton1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("My button was clicked, yay!");
}
}
}
I have a custom button control that does not derive from Button. Is it possible for me to implement the equivalent of IsDefault so that the command associated with my control will be invoked. I was hoping that this was an attached property that I could add to any control but as far as I can tell it doesn't seem to be. Am I out of luck if my control does not derive from Button or is there at least a reasonable workaround?
UPDATE:
I just took a peek with reflector at how this is being done underneath for Button and I must say it isn't the most self explanitory code I've seen. It appears that there are at least 3 dependency properties a few custom types just for the purpose of handling the concept of a Button being default. Since there doesn't seem to be an existing way to borrow the IsDefault functionality I suppose I'll have to narrow down what I'm trying to achieve so that I can at least get default focus and access key handling to work and just ignore the complexity invloved in the Button.IsDefault implementation.
UPDATE:
Added the following code example showing my uncessful attempt at trying itowlson's suggestions.
MyButton.xaml
<UserControl x:Class="IsDefault.MyButton"
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"
Height="28"
Width="117">
<Grid>
<Button Click="Button_Click">
<Button.Template>
<ControlTemplate>
<Border BorderThickness="2"
CornerRadius="12"
Background="DarkSlateBlue">
<TextBlock Foreground="WhiteSmoke"
HorizontalAlignment="Center"
VerticalAlignment="Center">Some Text</TextBlock>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
</UserControl>
MyButton.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace IsDefault
{
/// <summary>
/// Interaction logic for MyButton.xaml
/// </summary>
public partial class MyButton : UserControl
{
// Provide CLR accessors for the event
public event RoutedEventHandler Click
{
add { AddHandler(ClickEvent, value); }
remove { RemoveHandler(ClickEvent, value); }
}
// Using a RoutedEvent
public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent(
"Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyButton));
public bool IsDefault
{
get { return (bool)GetValue(IsDefaultProperty); }
set { SetValue(IsDefaultProperty, value); }
}
public static readonly DependencyProperty IsDefaultProperty =
DependencyProperty.Register(
"IsDefault",
typeof(bool),
typeof(MyButton),
new PropertyMetadata(false, IsDefault_PropertyChangedCallback, null));
public MyButton()
{
InitializeComponent();
}
protected override void OnAccessKey(AccessKeyEventArgs e)
{
base.OnAccessKey(e);
if (e.Key == "\r")
{
if (e.IsMultiple)
{
// There are multiple controls that are currently handling the Enter key
MessageBox.Show("there are multiple controls handling the Enter key.");
}
else
{
RaiseEvent(new RoutedEventArgs(ClickEvent, this));
}
}
}
private static void IsDefault_PropertyChangedCallback(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var button = d as MyButton;
var isDefault = (bool)e.NewValue;
if (isDefault)
{
AccessKeyManager.Register("\r", button);
}
else
{
AccessKeyManager.Unregister("\r", button);
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
RaiseEvent(new RoutedEventArgs(ClickEvent));
}
}
}
MainWindow.xaml
<Window x:Class="IsDefault.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:my="clr-namespace:IsDefault">
<Grid>
<Button Content="Button"
Height="23"
HorizontalAlignment="Left"
Margin="224,24,0,0"
Name="button1"
VerticalAlignment="Top"
Width="75" />
<TextBox Height="23"
HorizontalAlignment="Left"
Margin="208,94,0,0"
Name="textBox1"
VerticalAlignment="Top"
Width="120" />
<my:MyButton Height="28"
HorizontalAlignment="Left"
Margin="232,154,0,0"
x:Name="myButton1"
VerticalAlignment="Top"
Width="117"
Click="myButton1_Click"
IsDefault="True"/>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace IsDefault
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void myButton1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("My button was clicked, yay!");
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
设置 Button.IsDefault 所做的全部工作就是调用
AccessKeyManager.Register("\r", this)
(如果设置为 false,则调用 Unregister)。 (实际上,它在焦点管理方面做了一些额外的工作,但这对您来说可能并不重要。)因此,要自己实现类似的效果:
"\r"
(Enter 字符串)作为键,并将控件实例作为元素。All that setting Button.IsDefault does is call
AccessKeyManager.Register("\r", this)
(or Unregister if setting to false). (Actually, it does a little bit of extra work around focus management, but that's probably not crucial for you.)So to achieve a similar effect yourself:
"\r"
(the Enter string) as the key and the control instance as the element.尽管这是一个较老的问题,但其他人可能仍然像我一样对答案感兴趣。所以,这是我的解决方案。它基于我发现的 Microsoft 参考源的一些逆向工程(按钮和ButtonBase )。我还是 WPF 新手,所以可能不需要那么多代码,但它确实有效!
此代码向 UserControl 添加以下功能(它们似乎彼此紧密相连):
(代码中的所有注释均由 MS 做出)
Although this is an older question others may still be interested in an answer as I have been. So, here is my solution. It's based on some reverse engineering of Microsoft reference source I've found (Button and ButtonBase). I'm still newby in WPF so much code may be needless, but it works!
This code adds following features to an UserControl (they seem to be closly connected to each other):
(all comments in code are made by MS)