【问题标题】:Windows Phone 8 usercontrol imagebutton with different images for different statesWindows Phone 8 usercontrol imagebutton,不同状态的不同图像
【发布时间】:2013-09-26 15:28:22
【问题描述】:

我是 Windows Phone 8 开发的新手(你可以说知情的菜鸟在寻找答案),我正在寻求解决以下问题的帮助:

我有 usercontrol - 更具体地说是一个 包含图像和文本的按钮。我希望该按钮具有以下属性:

  • ContentImageNormal - 启用按钮时显示的图像
  • ContentImageDisabled - 禁用按钮时显示的图像

我现在拥有的是在我的项目中添加到文件夹 UserControls 中的用户控件,我可以使用它。我为它创建了一个样式并更改了禁用按钮的背景等。

我想知道什么:

  • 如何更改代码隐藏和其他所需的东西,以便我可以在下面根据需要使用它?

(我将在我的程序中使用该按钮六次,并且我想将此用户控件用作它的一种模板 - 准备它,因此我只需为两种状态指定 ImageSources,其余的将完成)

所需用法示例:

<UserControls:MainPageButton ContentText="Log In" ContentImageNormal="/ImagePathOrResourceETC.png" ContentImageDisabled="/ImagePathOrResourceETC.png"/>

我有 XAML:

    <UserControl x:Class="ProjectNameSpace.UserControls.MainPageButton">
        <Grid x:Name="LayoutRoot">
            <Button >
                <Button.Content>
                    <Grid>
                        ...omitted...
                        <Image x:Name="ContentImageHolder" ... />
                        <TextBlock x:Name="ContentTextBlock"  .../>
                    </Grid>
                </Button.Content>
            </Button>
        </Grid>
    </UserControl>

我目前拥有的 C# 代码隐藏:

...usings omitted...
public partial class MainPageButton : UserControl
{
    ...constructor and text setting omitted for brevity...
    public ImageSource ContentImage
    {
        get
        {
            return ContentImageHolder.Source;
        }
        set
        {
            ContentImageHolder.Source = value;
        }
    }
}
}

【问题讨论】:

  • 虽然我只会使用该按钮六次,但我需要做很多控制,所以这只是我目前最简单的情况。

标签: c# xaml user-controls windows-phone-8


【解决方案1】:

这很容易启动(嗯,对我来说,因为我有大量的 sn-ps 用于大多数困难的东西),所以这里是作为自定义 UserControl。

首先,使用您的图像创建用户控件。耶。

<UserControl x:Class="CustomImageControl.Controls.ThreeWay"
             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" >
    <Image
        x:Name="derpImage" />
</UserControl>

加倍努力,那里。

接下来,在代码隐藏中,您必须执行以下操作。首先,为代表您拥有的任何状态的每种类型的图像创建 DependencyProperties。在我的示例中,出于空间原因,我省略了 InactiveImage 和 DerpImage 属性。你可以看到 ActiveImage 是如何工作的。接下来,为控件的状态创建一个 DependencyProperty。在示例中,我有一个 State 枚举来定义它,但你可以做任何你想做的事情。在更改时,我会检查新值并根据需要更改图像。很简单。

public partial class ThreeWay : UserControl
{
    #region ActiveImage
    public static readonly DependencyProperty ActiveImageProperty =
        DependencyProperty.Register(
            "ActiveImage",
            typeof(ImageSource),
            typeof(ThreeWay),
            new UIPropertyMetadata());

    public ImageSource ActiveImage
    {
        get { return (ImageSource)GetValue(ActiveImageProperty); }
        set { SetValue(ActiveImageProperty, value); }
    }
    #endregion

    //InactiveImage and DerpImage snipped to keep this short

    #region ImageState
    public static readonly DependencyProperty ImageStateProperty =
        DependencyProperty.Register(
            "ImageState",
            typeof(State),
            typeof(ThreeWay),
            new UIPropertyMetadata(State.Derp, OnImageStatePropertyChanged));

    private static void OnImageStatePropertyChanged(
        DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        (d as ThreeWay).OnImageStateChanged(
          e.OldValue as State, e.NewValue as State);
    }

    private void OnImageStateChanged(State oldValue, State newValue)
    {
        switch(newValue)
        {
            case State.Active:
                this.derpImage.Source = ActiveImage;
                break;
            case State.Inactive:
                this.derpImage.Source = InactiveImage;
                break;
            case State.Derp:
                this.derpImage.Source = DerpImage;
                break;
        }
    }

    public State ImageState
    {
        get { return (State)GetValue(ImageStateProperty); }
        set { SetValue(ImageStateProperty, value); }
    }
    #endregion

    public ThreeWay()
    {
        InitializeComponent();
    }

    public enum State
    {
        Active,
        Inactive,
        Derp
    }
}

就是这样。你可以这样使用它:

<cc:ThreeWay xmlns:cc="clr-namespace:CustomImageControl.Controls"
             ActiveImage="active.png"
             InactiveImage="inactive.png"
             DerpImage="derp.jpg"
             ImageState="{Binding State}" />

这假定 DataContext 是一个实例,其属性名为 State,类型为 ThreeWay.State。如果没有,可以使用自定义转换器将任何(可为空的布尔?)转换为正确的类型。

【讨论】:

  • 谢谢,我正在慢慢找到一个解决方案,在该解决方案中我使用了按钮的 IsEnabledChangedEvent。您和@sexta13 的答案都非常有帮助。非常感谢。
【解决方案2】:

一月,

对于您的问题,您只是缺少一件事。

在您的 UserControl 中,您必须像这样创建 DependencyProperties:

 public static readonly DependencyProperty ContentImageNormalProperty =

            DependencyProperty.Register("ContentImageNormal",

                typeof(ImageSource ),

                typeof(MainPageButton));

那么你的属性应该是这样的:

     public string ContentImageNormal{ get { return (ImageSource)this.GetValue(ContentImageNormalProperty); } 
set { this.SetValue(ContentImageNormalProperty, value); } }

为您要设置的每个属性执行此操作...

请参阅此示例:http://stevenhollidge.blogspot.pt/2012/03/dependency-properties-in-user-control.html

希望对你有帮助。

问候, 塞尔吉奥·蒙泰罗

【讨论】:

  • 我给了威尔的回答接受的状态,虽然你的回答也很好,但他们都对我想做的事情有很大帮助。谢谢。
【解决方案3】:

看来您真正想做的是创建一个CustomControl 而不是UserControl。所以我将创建一个继承自 Button 的自定义控件,并使用按钮的视觉状态在两个图像之间切换。这是一个实现: 在 ThemeS/Generic.xaml 中添加此样式

<Style  TargetType="ButtonWithImage">
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="BorderBrush" Value="{StaticResource PhoneForegroundBrush}"/>
        <Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>
        <Setter Property="BorderThickness" Value="{StaticResource PhoneBorderThickness}"/>
        <Setter Property="FontFamily" Value="{StaticResource PhoneFontFamilySemiBold}"/>
        <Setter Property="FontSize" Value="{StaticResource PhoneFontSizeMedium}"/>
        <Setter Property="Padding" Value="10,5,10,6"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Grid Background="Transparent">

                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal"/>
                                <VisualState x:Name="MouseOver"/>
                                <VisualState x:Name="Pressed">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentContainer">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneButtonBasePressedForegroundBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="ButtonBackground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneAccentBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Disabled">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentContainer">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneDisabledBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="ButtonBackground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PhoneDisabledBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="ButtonBackground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Transparent"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="enablebleImage">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="disableImage">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <Border x:Name="ButtonBackground" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="0" Margin="{StaticResource PhoneTouchTargetOverhang}">
                            <Grid>
                                <Image x:Name="disableImage" Source="{TemplateBinding EnableImageSource}" Visibility="Collapsed"/>
                                <Image x:Name="enablebleImage"  Source="{TemplateBinding DisableImageSource}"/>
                              <ContentControl x:Name="ContentContainer" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" Padding="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
                            </Grid>
                        </Border>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

并定义以下类:

public class ButtonWithImage :Button
{
    public static readonly DependencyProperty DisableImageSourceProperty =
        DependencyProperty.Register("DisableImageSource", typeof (ImageSource), typeof (ButtonWithImage), new PropertyMetadata(default(ImageSource)));

    public ImageSource DisableImageSource
    {
        get { return (ImageSource) GetValue(DisableImageSourceProperty); }
        set { SetValue(DisableImageSourceProperty, value); }
    }

    public static readonly DependencyProperty EnableImageSourceProperty =
        DependencyProperty.Register("EnableImageSource", typeof (ImageSource), typeof (ButtonWithImage), new PropertyMetadata(default(ImageSource)));

    public ImageSource EnableImageSource
    {
        get { return (ImageSource) GetValue(EnableImageSourceProperty); }
        set { SetValue(EnableImageSourceProperty, value); }
    }


    public ButtonWithImage()
    {
        this.DefaultStyleKey = typeof (Button);

    }
}

【讨论】:

  • 有没有更好的方法来创建自定义控件?我想在它自己的文件中创建它,最好放在一起的地方,这样会更容易管理(并将此代码移动到另一个项目 - 我不需要库,只要它很容易做到)
  • 要应用于您的所有 ButtonWithImage 实例,样式需要在 Themes/Generic.xaml 中定义,但该类可以在任何地方定义,因此您也可以在 Themes 文件夹中创建它。否则,如果您在 Generic.xaml 中已经有其他内容,那么唯一的选择是在资源字典中定义样式,但这意味着您需要在 App.xaml 中对此 ResourceDictionary 进行“引用”,并且每次您使用 ButtonWithImage 你需要指定 Style="{StaticResource myButtonWithImageStyle}"
  • 如果你想在项目之间共享控制,老实说创建一个库可能是最容易的。您只需要创建一个库项目,在 Themes/Generic.xaml 中添加样式,创建 ButtonWithImage 类,就可以了
【解决方案4】:

您可以简单地绑定到 Image 和 Text。

你的例子:

<Button name="buttonTest">
                <Button.Content>
                    <Grid>
                        ...omitted...
                        <Image x:Name="ContentImageHolder" Source="{Binding ElementName=buttonTest, Path=IsEnabled, Converter ={StaticResource convertButton}}" Grid.Row="0" />
                        <TextBlock x:Name="ContentTextBlock"  Text="My Sample Text" Grid.Row="1" FontSize="{StaticResource PhoneFontSizeNormal}" FontFamily="{StaticResource PhoneFontFamilyNormal}" Foreground="{StaticResource PhoneForegroundBrush}" TextAlignment="Center"/>
                    </Grid>

然后您将创建一个转换器,该转换器将接收 True 或 False(启用或未启用)作为值,然后在每个状态下返回您想要的图像。

您的转换器可能类似于

  public class ButtonImageConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {


            if((bool) value)
return  CreateImage("pack://application:,,,/ButtonEnabled.ico"); // url should be correct
else  return  CreateImage("pack://application:,,,/ButtonDisable.ico");

        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return null;
        }

        /// <summary>
        /// Method to create an image to bind to icons in treeview
        /// </summary>
        /// <param name="uriToImage">uri to image in folder.</param>
        /// <returns>a bitmap image to bind to image source in tree view</returns>
        private BitmapImage CreateImage(string uriToImage)
        {
            if (!string.IsNullOrEmpty(uriToImage))
            {
                BitmapImage genericBitmap = new BitmapImage();
                genericBitmap.BeginInit();
                genericBitmap.UriSource = new Uri(uriToImage);
                genericBitmap.EndInit();
                return genericBitmap;
            }

            return null;

        }
    }

对于文本,如果它依赖于任何属性,您也可以这样做。

【讨论】:

  • 呃,如果他不创建自定义控件,这将是最好的方法。但是,既然他是,那么他所需要的只是一点点代码隐藏来完全按照您在转换器中所做的事情。为每个状态的图像添加 DependencyProperty 并为状态本身添加一个 DependencyProperty 将是一个不错的选择。
  • @Will :你能更详细地描述一下吗?我想我明白你的意思,但我正在慢慢到达那里。另外,我编辑/重新安排了我的问题,因为我的意图并不明确。
猜你喜欢
  • 2013-02-02
  • 2013-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-11
  • 1970-01-01
  • 1970-01-01
  • 2012-06-20
相关资源
最近更新 更多