Служба кнопок Silverlight 4 по умолчанию
В течение нескольких месяцев я успешно использовал пример кнопки по умолчанию David Justices в своем приложении SL 3. Этот подход основан на прикрепленном свойстве.
После обновления до SL4 подход больше не работает, и я получаю исключение XAML:
Неизвестная ошибка синтаксического анализатора: сканер 2148474880
Кто-нибудь успешно использовал это (или любое другое) поведение по умолчанию, прикрепленное к кнопке в SL4?
Есть ли другой способ добиться поведения кнопки по умолчанию в SL4 с помощью новых доступных классов?
Спасибо Марк
5 ответов
Я расширил подход Дэвида, позволив задать дополнительный ключ (по умолчанию Enter) в дополнительном свойстве:
public static DependencyProperty ButtonKeyProperty = DependencyProperty.RegisterAttached(
"ButtonKey",
typeof(Key),
typeof(Defaults),
new PropertyMetadata(Key.Enter, ButtonChanged));
public static void SetButtonKey(DependencyObject dependencyObj, Key key)
{
dependencyObj.SetValue(ButtonKeyProperty, key);
}
public static Key GetButtonKey(DependencyObject dependencyObj)
{
return (Key)dependencyObj.GetValue(ButtonKeyProperty);
}
Я изменил исходное свойство, чтобы затем использовать это свойство:
Key key = GetButtonKey(dependencyObj);
if (button.IsEnabled && keyEvent.Key == key)
...
Так что теперь, например, я могу использовать Escape в качестве ключа, если я хочу (заметьте, я изменил имена классов и свойств):
... UI:Defaults.Button="{Binding ElementName=myButton}" UI:Defaults.ButtonKey="Escape" ...
Окончательное решение для нас также должно было обойти проблему, когда свойство поддержки не обновлялось до нажатия кнопки (как во всех шаблонах MVVM)...Примечание: peer.SetFocus();
Изменить: Добавлен пример XAML.
public static class DefaultButtonService
{
public static DependencyProperty DefaultButtonProperty =
DependencyProperty.RegisterAttached("DefaultButton",
typeof(Button),
typeof(DefaultButtonService),
new PropertyMetadata(null, DefaultButtonChanged));
private static void DefaultButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
var uiElement = d as UIElement;
var button = e.NewValue as Button;
if (uiElement != null && button != null) {
uiElement.KeyUp += (sender, arg) => {
var peer = new ButtonAutomationPeer(button);
if (arg.Key == Key.Enter) {
peer.SetFocus();
uiElement.Dispatcher.BeginInvoke((Action)delegate {
var invokeProv =
peer.GetPattern(PatternInterface.Invoke) as IInvokeProvider;
if (invokeProv != null)
invokeProv.Invoke();
});
}
};
}
}
public static Button GetDefaultButton(UIElement obj) {
return (Button)obj.GetValue(DefaultButtonProperty);
}
public static void SetDefaultButton(DependencyObject obj, Button button) {
obj.SetValue(DefaultButtonProperty, button);
}
}
Как подать заявку в XAML:
<StackPanel>
<TextBox DinnerConfig:DefaultButtonService.DefaultButton="{Binding ElementName=MyButton}"
Text="Press Enter" />
<Button x:Name="MyButton"
Content="Click me" />
</StackPanel>
Я действительно надеялся, что в Silverlight 4 будет готовое решение для такого распространенного варианта использования, но, к сожалению, я не думаю, что оно есть.
Есть еще одна реализация кнопки по умолчанию от Патрика Колдуэлла. Он также использует Attached Properties.
Я проверил это в приложении SL 4, и, похоже, он справился с работой.
Вы можете найти код здесь: http://www.cauldwell.net/patrick/blog/DefaultButtonSemanticsInSilverlightRevisited.aspx
Редактировать: я настроил код Дэвида Джастиса, чтобы он работал для Silverlight 4. Я только что изменил GetDefaultButton и SetDefaultButton, чтобы принимать и возвращать DefaultButtonService. Использование такое же, как отмечено на его сайте. Это должно работать для вас:
Edit2: добавлен пример XAML для ясности.
public class DefaultButtonService
{
public static DependencyProperty DefaultButtonProperty =
DependencyProperty.RegisterAttached("DefaultButton",
typeof(Button),
typeof(DefaultButtonService),
new PropertyMetadata(null, DefaultButtonChanged));
private static void DefaultButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var uiElement = d as UIElement;
var button = e.NewValue as Button;
if (uiElement != null && button != null)
{
uiElement.KeyUp += (sender, arg) =>
{
if (arg.Key == Key.Enter)
{
var peer = new ButtonAutomationPeer(button);
var invokeProv =
peer.GetPattern(PatternInterface.Invoke) as IInvokeProvider;
if (invokeProv != null)
invokeProv.Invoke();
}
};
}
}
public static DefaultButtonService GetDefaultButton(UIElement obj)
{
return (DefaultButtonService)obj.GetValue(DefaultButtonProperty);
}
public static void SetDefaultButton(DependencyObject obj, DefaultButtonService button)
{
obj.SetValue(DefaultButtonProperty, button);
}
}
Как подать заявку в XAML:
<StackPanel>
<TextBox DinnerConfig:DefaultButtonService.DefaultButton="{Binding ElementName=MyButton}"
Text="Press Enter" />
<Button x:Name="MyButton"
Content="Click me" />
</StackPanel>
Проблема была вызвана плохо напечатанным GetDefaultButton
метод. Это должно было быть напечатано как Button
Например, используя:
public static Button GetDefaultButton(UIElement obj)
{
return (Button)obj.GetValue(DefaultButtonProperty);
}
вместо
public static DefaultButtonService GetDefaultButton(UIElement obj)
{
return (DefaultButtonService)obj.GetValue(DefaultButtonProperty);
}
работает как положено.
HTH кто-то еще,
отметка
Вот простой способ подключить кнопку по умолчанию при нажатии Enter:
По соображениям безопасности Silverlight не позволит вам вызывать обработчик кнопок непосредственно в коде, поэтому мы должны создать новую подпрограмму, которую можно вызывать, а затем обработчик кнопок и обработчик клавиш формы вызовут эту подпрограмму.
Шаг 1: создайте процедуру и поместите в нее все, что было у кнопки
private void DoSharedRoutine(){ // do something }
Шаг 2: иметь обработчик кнопки вызова общей процедуры
private void Login_Click(object sender, System.Windows.RoutedEventArgs e)
{
DoSharedRoutine();
}
Шаг 3: у вас есть обработчик keydown вашей панели, который содержит вашу кнопку вызова общей процедуры
private void MyGrid_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == Key.Enter) DoSharedRoutine();
}