【问题标题】:How to modify Button for Multitouch support on Windows Phone?如何在 Windows Phone 上修改 Button 以支持多点触控?
【发布时间】:2011-09-05 14:27:38
【问题描述】:

我想要一个响应 Touch.FrameReported Up & Down 事件的按钮,而不是通常使用的 MouseDown 和 MouseUp 事件,这样这个按钮就可以在 Windows Phone 上作为另一个按钮同时使用。 我已经有一个具有 MouseDown 和 MouseUp 状态的自定义 Button 控件,但我不确定如何使那里的 Up 和 Down 事件触发正确的外观 - 可能需要设置 VisualStateManager 的某些东西,但无法弄清楚如何使用它 - 解决方案需要使用标准 Button 控件,因为我只是将其扩展为两种状态 - 作为具有正常和“按下”状态的按钮控件。
这是针对较大 Silverlight 项目中的游戏屏幕,项目的其余部分是标准 Silverlight,带有标准按钮及其正常行为,但是在一个地方需要多点触控,因此这不能是 XNA 项目,因为这需要将 99% 的应用程序移植到不支持使用的其他功能的 XNA - 我已经能够扩展自定义控件以支持多点触控,但希望按钮也以这种方式做出反应 - 另外我相信这对其他人有用,尤其是因为这很可能也适用于 Windows 7/8 开发。


编辑:这是我的按钮的代码和 Generic.xaml,具有正常行为 (OnMouseUp/OnMouseDown)

代码:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows.Input;
using System.Diagnostics;

namespace UXLibrary
{
    [TemplatePart(Name = "Pressed", Type = typeof(BitmapSource))]
    [TemplatePart(Name = "Normal", Type = typeof(BitmapSource))]
    public class UXButton : Button
    {
        public static readonly DependencyProperty  PressedProperty =
        DependencyProperty.Register("Pressed", typeof(BitmapSource),
        typeof(UXButton), null);

        public static readonly DependencyProperty NormalProperty =
        DependencyProperty.Register("Normal", typeof(BitmapSource),
        typeof(UXButton), null);

        public BitmapSource Pressed
        {
            get { return (BitmapSource)GetValue(PressedProperty); }
            set { SetValue(PressedProperty, value); }
        }

        public BitmapSource Normal
        {
            get { return (BitmapSource)GetValue(NormalProperty); }
            set { SetValue(NormalProperty, value); }
        }

        /// <summary>Constructor</summary>
        public UXButton()
        {
            DefaultStyleKey = typeof(UXButton);
        }

        /// <summary>OnApplyTemplate</summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();     
        }

    }
}

Generic.xaml

<Style TargetType="local:UXButton">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:UXButton">
                    <Grid>
                        <vsm:VisualStateManager.VisualStateGroups>
                            <vsm:VisualStateGroup x:Name="CommonStates">
                                <vsm:VisualState x:Name="Disabled">
                                    <Storyboard>
                                        <DoubleAnimation Duration="0" Storyboard.TargetName="NormalImage" Storyboard.TargetProperty="(UIElement.Opacity)" To="0.5"/>
                                    </Storyboard>
                                </vsm:VisualState>
                                <vsm:VisualState x:Name="Normal">
                                    <Storyboard>
                                        <DoubleAnimation Duration="0" Storyboard.TargetName="NormalImage" Storyboard.TargetProperty="(UIElement.Opacity)" To="1"/>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PressedImage" Storyboard.TargetProperty="(UIElement.Visibility)">
                                            <ObjectAnimationUsingKeyFrames.KeyFrames>
                                                <DiscreteObjectKeyFrame KeyTime="00:00:00">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Collapsed</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames.KeyFrames>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="NormalImage" Storyboard.TargetProperty="(UIElement.Visibility)">
                                            <ObjectAnimationUsingKeyFrames.KeyFrames>
                                                <DiscreteObjectKeyFrame KeyTime="00:00:00">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Visible</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames.KeyFrames>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </vsm:VisualState>
                                <vsm:VisualState x:Name="MouseOver"/>
                                <vsm:VisualState x:Name="Pressed">
                                    <Storyboard>
                                        <DoubleAnimation Duration="0" Storyboard.TargetName="NormalImage" Storyboard.TargetProperty="(UIElement.Opacity)" To="1"/>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PressedImage" Storyboard.TargetProperty="(UIElement.Visibility)">
                                            <ObjectAnimationUsingKeyFrames.KeyFrames>
                                                <DiscreteObjectKeyFrame KeyTime="00:00:00">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Visible</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames.KeyFrames>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="NormalImage" Storyboard.TargetProperty="(UIElement.Visibility)">
                                            <ObjectAnimationUsingKeyFrames.KeyFrames>
                                                <DiscreteObjectKeyFrame KeyTime="00:00:00">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Collapsed</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames.KeyFrames>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </vsm:VisualState>
                            </vsm:VisualStateGroup>
                            <vsm:VisualStateGroup x:Name="FocusStates">
                                <vsm:VisualState x:Name="Focused"/>
                                <vsm:VisualState x:Name="Unfocused"/>
                            </vsm:VisualStateGroup>
                        </vsm:VisualStateManager.VisualStateGroups>
                        <Image x:Name="PressedImage" Stretch="Uniform" Source="{TemplateBinding Pressed}"/>
                        <Image x:Name="NormalImage" Stretch="Uniform" Source="{TemplateBinding Normal}"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

解决方案

<Style TargetType="local:UXButton">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:UXButton">
                    <Grid>
                        <vsm:VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="MultiTouchStates">
                                <vsm:VisualState x:Name="Normal">
                                    <Storyboard>
                                        <DoubleAnimation Duration="0" Storyboard.TargetName="NormalImage" Storyboard.TargetProperty="(UIElement.Opacity)" To="1"/>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PressedImage" Storyboard.TargetProperty="(UIElement.Visibility)">
                                            <ObjectAnimationUsingKeyFrames.KeyFrames>
                                                <DiscreteObjectKeyFrame KeyTime="00:00:00">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Collapsed</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames.KeyFrames>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="NormalImage" Storyboard.TargetProperty="(UIElement.Visibility)">
                                            <ObjectAnimationUsingKeyFrames.KeyFrames>
                                                <DiscreteObjectKeyFrame KeyTime="00:00:00">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Visible</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames.KeyFrames>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </vsm:VisualState>
                                <VisualState x:Name="SpecialTouch">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="PressedImage">
                                            <DiscreteObjectKeyFrame KeyTime="0">
                                                <DiscreteObjectKeyFrame.Value>
                                                    <Visibility>Visible</Visibility>
                                                </DiscreteObjectKeyFrame.Value>
                                            </DiscreteObjectKeyFrame>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="NormalImage">
                                            <DiscreteObjectKeyFrame KeyTime="0">
                                                <DiscreteObjectKeyFrame.Value>
                                                    <Visibility>Collapsed</Visibility>
                                                </DiscreteObjectKeyFrame.Value>
                                            </DiscreteObjectKeyFrame>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </vsm:VisualStateManager.VisualStateGroups>
                        <Image x:Name="PressedImage" Stretch="Uniform" Source="{TemplateBinding Pressed}"/>
                        <Image x:Name="NormalImage" Stretch="Uniform" Source="{TemplateBinding Normal}"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

代码:

/// <summary>Button</summary>
[TemplatePart(Name = "Wrapper", Type = typeof(Grid))]
[TemplateVisualState(Name = "SpecialTouch", GroupName = "MultiTouchStates")]
public class UXButton : Button
{
    public static readonly DependencyProperty PressedProperty =
    DependencyProperty.Register("Pressed", typeof(BitmapSource),
    typeof(UXButton), null);

    public static readonly DependencyProperty NormalProperty =
    DependencyProperty.Register("Normal", typeof(BitmapSource),
    typeof(UXButton), null);

    public BitmapSource Pressed
    {
        get { return (BitmapSource)GetValue(PressedProperty); }
        set { SetValue(PressedProperty, value); }
    }

    public BitmapSource Normal
    {
        get { return (BitmapSource)GetValue(NormalProperty); }
        set { SetValue(NormalProperty, value); }
    }

    /// <summary>Constructor</summary>
    public UXButton()
    {
        DefaultStyleKey = typeof(UXButton);

    }

    /// <summary>OnApplyTemplate</summary>
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        Touch.FrameReported += (object sender, TouchFrameEventArgs e) =>
        {
            Image pressed = (Image)GetTemplateChild("PressedImage");
            Image normal = (Image)GetTemplateChild("NormalImage");
            TouchPointCollection points = e.GetTouchPoints(null);
            foreach (TouchPoint point in points)
            {
                if (point.Action == TouchAction.Down && (point.TouchDevice.DirectlyOver == normal || point.TouchDevice.DirectlyOver == pressed))
                {
                    VisualStateManager.GoToState(this, "SpecialTouch", false);
                }
                else if (point.Action == TouchAction.Up)
                {
                    VisualStateManager.GoToState(this, "Normal", false);
                }
            } 
        };
    }
}

【问题讨论】:

  • 这个游戏不是用 XNA 制作的吗?
  • 为什么总是被问到这个问题,应该在我以后的问题中补充说它不在 XNA 中!但这并不是一个好点,它比 Silverlight 更好地支持多点触控 - 但这是为使用标准控件的 Silverlight 项目添加一些功能 - 但在一个特定的地方他们需要支持多点触控 - 没有其他地方。跨度>
  • 你能展示一下你到目前为止做了什么吗?
  • 我现在已经为我的按钮添加了代码,谢谢!
  • 已将解决方案添加到我的问题中,希望这对其他人有帮助

标签: c# silverlight windows-phone-7 button multi-touch


【解决方案1】:

如果我正确理解您的问题,我认为您需要再创建一个视觉状态,而不是两个部分(“按下”和“正常”)。

// UPDATE: you need to get the Grid in order to know the touch area
[TemplatePart(Name = "Wrapper", Type = typeof(Grid))]
[TemplateVisualState(Name = "SpecialTouch", GroupName = "MultiTouchStates")]
public class UXButton : Button

然后在自定义按钮的构造函数中,订阅 FrameReported 事件,

    public UXButton()
    {
        DefaultStyleKey = typeof(UXButton);

        Touch.FrameReported += new TouchFrameEventHandler(Touch_FrameReported);
    }

    void Touch_FrameReported(object sender, TouchFrameEventArgs e)
    {
        // UPDATE: get the Grid
        var wrapper = GetTemplateChild("Wrapper") as Grid;

        TouchPointCollection points = e.GetTouchPoints(null);

        foreach (TouchPoint point in points)
        {
            // UPDATE: also do the touch area check here
            // specify what touch you want
            if (point.Action == TouchAction.Down && point.TouchDevice.DirectlyOver == wrapper)
            {
                VisualStateManager.GoToState(this, "SpecialTouch", false);
            }
        }
    }

然后在样式中,您可以在刚刚创建的这种视觉状态下隐藏和显示图像。如果您希望能够动态更改正常和按下的图像,当然您可以重新添加您的 TemplateParts。

更新:您还需要为 Grid 的根元素指定名称和背景颜色, 像这样,

<Grid x:Name="Wrapper" Background="Transparent">

                        <VisualStateGroup x:Name="MultiTouchStates">
                            <VisualState x:Name="SpecialTouch">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="PressedImage">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Visible</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="NormalImage">
                                        <DiscreteObjectKeyFrame KeyTime="0">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Collapsed</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>

希望这会有所帮助。

【讨论】:

  • 这与我尝试过的类似 - 为 TouchAction.Up 添加另一个状态为 MouseUp 会产生所需的效果,但是当使用多个按钮时,无论我尝试什么,它们都会一起改变状态 - e.GetTouchPoints(空值);或 e.GetTouchPoints(this);没有区别 - 只需要每个按钮独立于其他按钮对其做出反应 - 他们对自己的触摸事件做出反应
  • 你的意思是你触摸了一个按钮,其他按钮都改变了它们的状态?
  • 是的 - 我可以让一个自己处理它,但是添加更多,其他按钮有时会在点击一个时触发向下状态 - 例如,我有三个彼此相邻并点击中间一个使左右按钮进入向下状态 - 有时它会起作用,但更多的点击会产生来自其他按钮的多个反应。我正在 Windows Phone 设备上对此进行测试,因为无论如何我都没有其他方法可以进行多点触控!
  • 嗯...你会不会不小心点击了其他按钮的边缘?尝试增加边距,看看是否仍然发生?
  • 抱歉忘记打分了,还以为是我标记为答案时发生的!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-10
  • 2011-03-29
  • 2011-10-24
  • 1970-01-01
  • 2016-04-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多