Silverlight 4 Default Button Service

限于喜欢 提交于 2019-12-20 10:54:34

问题


For a few months I have been successfully using David Justices Default Button example in my SL 3 app. This approach is based on an attached property.

After upgrading to SL4, the approach no longer works, and I get a XAML exception:

Unknown parser error: Scanner 2148474880

Has anyone succesfully used this (or any other) default button attached behaviours in SL4?

Is there any other way to achieve default button behaviour in SL4 with the new classes that are available?

Thanks, Mark


回答1:


I extended David's approach by allowing a custom key (defaulted to Enter) to be set in an additional property:

    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);
    }

I modified the original property to then leverage this property:

    Key key = GetButtonKey(dependencyObj);
    if (button.IsEnabled && keyEvent.Key == key)
        ...

So now, for example, I can use Escape as the key if I want (note I changed the named of the classes and properties):

    ... UI:Defaults.Button="{Binding ElementName=myButton}" UI:Defaults.ButtonKey="Escape" ...



回答2:


The final solution for us also had to get around the issue where the backing property was not being updated prior to the button click occuring (as in all MVVM patterns).... Note: peer.SetFocus();

Edit: Added XAML example.

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);
    }       
}

How to apply in XAML:

<StackPanel>
    <TextBox DinnerConfig:DefaultButtonService.DefaultButton="{Binding ElementName=MyButton}"
                Text="Press Enter" />
    <Button x:Name="MyButton"
            Content="Click me" />
</StackPanel>



回答3:


I was really hoping that there would be a out of the box solution for such a common use-case in Silverlight 4, but unfortunately I don't think there is.

There is another Default Button implementation by Patrick Cauldwell. He's also using Attached Properties.

I've tested this in a SL 4 application and it seems to do the job.

You can find the code here: http://www.cauldwell.net/patrick/blog/DefaultButtonSemanticsInSilverlightRevisited.aspx

Edit: I've tweaked David Justice's code to get it working for Silverlight 4. I've just changed the GetDefaultButton and SetDefaultButton to take and return a DefaultButtonService. Usage is the same as noted on his website. This should work for you:

Edit2: Added XAML example for clarity.

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);
        }        
    }

How to apply in XAML:

<StackPanel>
    <TextBox DinnerConfig:DefaultButtonService.DefaultButton="{Binding ElementName=MyButton}"
                Text="Press Enter" />
    <Button x:Name="MyButton"
            Content="Click me" />
</StackPanel>



回答4:


The issue was caused by a poorly typed GetDefaultButton method. This should have been typed as Button, for example using:

    public static Button GetDefaultButton(UIElement obj)
    {
        return (Button)obj.GetValue(DefaultButtonProperty);
    }

instead of

    public static DefaultButtonService GetDefaultButton(UIElement obj)    
    {    
        return (DefaultButtonService)obj.GetValue(DefaultButtonProperty);    
    } 

works as expected.

HTH someone else,
Mark




回答5:


Here is an easy way to wire a default button when enter is pressed:

For security reasons, silverlight will not let you call a button handler directly in code, so we have to create a new routine that can be called, then have the button handler and the form keydown handler both call this routine.

Step 1: make a routine and put everything in it that the button routine had

private void DoSharedRoutine(){ // do something }

Step 2: have button handler call shared routine

private void Login_Click(object sender, System.Windows.RoutedEventArgs e)
    {
        DoSharedRoutine();
    }

Step 3: have keydown handler of your panel that contains your button call the shared routine

private void MyGrid_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
     {
         if (e.Key == Key.Enter) DoSharedRoutine();
     }


来源:https://stackoverflow.com/questions/2683891/silverlight-4-default-button-service

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!