10

Silverlight4とWPF4を使用して、ボタンをマウスオーバーしたときに、含まれているテキストのテキストの色を変更するボタンスタイルを作成しようとしています。これをSilverlightとWPFの両方と互換性を持たせようとしているので、ビジュアル状態マネージャーを使用しています。

<Style TargetType="{x:Type Button}">
<Setter Property="Template">
    <Setter.Value>
        <ControlTemplate TargetType="Button">
            <Border x:Name="outerBorder" CornerRadius="4" BorderThickness="1" BorderBrush="#FF757679">
                <VisualStateManager.VisualStateGroups>
                    <VisualStateGroup x:Name="CommonStates">
                        <VisualState x:Name="Normal" />
                        <VisualState x:Name="MouseOver">
                            <Storyboard>
                                <ColorAnimation Duration="0" To="#FFFEFEFE"
                                                Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)"
                                                Storyboard.TargetName="contentPresenter"/> 
                            </Storyboard>
                        </VisualState>
                    </VisualStateGroup>
                </VisualStateManager.VisualStateGroups>
                <Grid>
                    <Border x:Name="Background" CornerRadius="3" BorderThickness="1" BorderBrush="Transparent">
                        <Grid>
                            <ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}"/>
                        </Grid>
                    </Border>
                </Grid>
            </Border>
        </ControlTemplate>
    </Setter.Value>
</Setter>

これは通常の古いボタンのテンプレートであるため、その中にテキストブロックが含まれている保証はなく、最初はこれが可能かどうかさえわかりませんでした。不思議なことに、ボタンが次のように宣言されている場合、テキストの色は変わります。

<Button Content="Hello, World!" />

ただし、ボタンが次のように宣言されている場合は変更されません。

<Button>
    <TextBlock Text="Hello, World!" /> <!-- Same result with <TextBlock>Hello, World </TextBlock> -->
</Button>

ビジュアルツリー(スヌープで検査した場合)は同じですが([ボタン]-> [ContentPresenter]-> [TextBlock])、最初のバージョンで作成されたテキストブロックのデータコンテキストは「Hello、World」に設定されていますが、 2番目のバージョンでは、テキストプロパティが設定されているだけです。これは、コントロールの作成順序と関係があると思います(最初のバージョンでは、ボタンがTextBlockを作成し、2番目のバージョンでは、テキストブロックが最初に作成される可能性がありますか?これについてはよくわかりません)。

これを調査する過程で、Silverlightで機能するソリューション(ContentPresenterをContentControlに置き換えるなど)をいくつか見ましたが、WPFでは機能しません(プログラムが実際にクラッシュします)。

これはボタンのコントロールテンプレートにあり、可能であればVSMを使用したいので、ボタン自体のForegroundプロパティを明示的に変更することも除外されると思います(テンプレート内からアクセスする方法がわかりませんか? )。

私は本当に助け、誰もが与えることができるアドバイスをいただければ幸いです。

4

3 に答える 3

6

もう少し考えた結果、最終的にたどり着いた解決策は、ボタンのコントロール テンプレート内の ContentPresenter 要素に添付プロパティを追加することです。添付プロパティは Color を受け入れ、設定時に TextBlocks のコンテンツ プレゼンターのビジュアル ツリーを調べ、その Foreground プロパティを渡された値に設定します。私が必要とするもののために。

public static class ButtonAttachedProperties
    {
        /// <summary>
        /// ButtonTextForegroundProperty is a property used to adjust the color of text contained within the button.
        /// </summary>
        public static readonly DependencyProperty ButtonTextForegroundProperty = DependencyProperty.RegisterAttached(
            "ButtonTextForeground",
            typeof(Color),
            typeof(FrameworkElement),
            new FrameworkPropertyMetadata(Color.FromArgb(255, 0, 0, 0), FrameworkPropertyMetadataOptions.AffectsRender, OnButtonTextForegroundChanged));

        public static void OnButtonTextForegroundChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue is Color)
            {
                var brush = new SolidColorBrush(((Color) e.NewValue)) as Brush;
                if (brush != null)
                {
                    SetTextBlockForegroundColor(o as FrameworkElement, brush);
                }
            }
        }

        public static void SetButtonTextForeground(FrameworkElement fe, Color color)
        {
            var brush = new SolidColorBrush(color);
            SetTextBlockForegroundColor(fe, brush);
        }

        public static void SetTextBlockForegroundColor(FrameworkElement fe, Brush brush)
        {
            if (fe == null)
            {
                return;
            }

            if (fe is TextBlock)
            {
                ((TextBlock)fe).Foreground = brush;
            }

            var children = VisualTreeHelper.GetChildrenCount(fe);
            if (children > 0)
            {
                for (int i = 0; i < children; i++)
                {
                    var child = VisualTreeHelper.GetChild(fe, i) as FrameworkElement;
                    if (child != null)
                    {
                        SetTextBlockForegroundColor(child, brush);
                    }
                }
            }
            else if (fe is ContentPresenter)
            {
                SetTextBlockForegroundColor(((ContentPresenter)fe).Content as FrameworkElement, brush);
            }
        }
    }

テンプレートを次のように変更しました。

<ContentPresenter x:Name="contentPresenter" 
                  ContentTemplate="{TemplateBinding ContentTemplate}" 
                  local:ButtonAttachedProperties.ButtonTextForeground="{StaticResource ButtonTextNormalColor}" />
于 2010-10-05T17:51:41.207 に答える
5

これはトリッキーです。前景は、コンテンツ プレゼンターが作成するコントロールに渡されるボタンのプロパティです。これは依存関係プロパティであり、テンプレートで使用できるコントロールのプロパティではないため、純粋な xaml を使用してアニメーション化することは困難です。

MSDN には、前景色を変更する方法に関するいくつかのサンプルがあります。ただし、コードからこの作業を行いたいとは思えません。(http://msdn.microsoft.com/en-us/library/system.windows.controls.button(VS.95).aspx)

ボタンのデフォルトのテンプレートが足を引っ張っています。お気づきのとおり、ボタンはコンテンツのないコントロールです。つまり、デザイナーはランダムな視覚オブジェクトをその中に押し込むことができます。コンテンツがない場合、色を設定する保証されたコンポーネントがないため、フォアグラウンド プロパティはコントロールではなくテンプレートのメンバーになります。これが、その中にコンテンツプレゼンターがある理由です。

したがって、2 つのオプションがあります。1. 簡単だが柔軟ではない (純粋な xaml) 2. ボタンが行うすべてのことを行う独自のコントロールを作成する (コードと多くのテストが必要)

私はあなたのために#1を実装します。

ボタンのテンプレートを変更してコンテンツ プレゼンターを削除すると、その中に 2 つのテキストブロックを配置できます。1 つは通常の色、もう 1 つはマウス オーバー カラーです。

<TextBlock x:Name="RedBlock" Text="{TemplateBinding Content}"      HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Foreground="Red" Visibility="Collapsed"/>       
<TextBlock x:Name="BlueBlock" Text="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Foreground="#FF0027FF"/> 

Textblock の Text プロパティがボタンのコンテンツにバインドされていることに注意してください。テキスト以外のものにバインドする必要がある場合、これは問題になります。TextBlocks は、AlphaNumeric 値以外は何も表示できません。

また、デフォルトで RedBlock の可視性を折りたたんでいることにも注意してください。

次に、私MouseOver VisualState's Storyboardの場合、RedBlock が表示されるようにアニメーション化し、BlueBlock が非表示になるようにアニメーション化できます。

<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="RedBlock">
    <DiscreteObjectKeyFrame KeyTime="0">
        <DiscreteObjectKeyFrame.Value>
            <Visibility>Visible</Visibility>
        </DiscreteObjectKeyFrame.Value>
    </DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="BlueBlock">
    <DiscreteObjectKeyFrame KeyTime="0">
        <DiscreteObjectKeyFrame.Value>
            <Visibility>Collapsed</Visibility>
        </DiscreteObjectKeyFrame.Value>
    </DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>

ハックのように感じます。おそらく、このボタンを多くの場所に実装することはないでしょう。バインドする適切な DependencyProperties を使用して、独自のボタンを作成したいと思います。HighlightForground と Text にそれぞれ 1 つ。また、誰かがコンテンツを AlphaNumeric 値以外に設定しようとした場合は、非表示にするか、少なくとも例外をスローしたいと思います。ただし、XAML は同じで、視覚状態ごとに異なるテキストブロックを使用します。

これが役立つことを願っています。

すてきな一日を。

-エレミヤ

于 2010-10-05T16:13:58.037 に答える
0

これをテンプレートで使用していますが、正常に動作します

<ControlTemplate TargetType="Button">
                <Border 
                    Margin="{TemplateBinding Margin}"
                    BorderBrush="{StaticResource BorderBrush}"
                    BorderThickness="{TemplateBinding BorderThickness}" 
                    CornerRadius="2"
                    >

                    <TextBlock 
                        Margin="{TemplateBinding Padding}"
                        Text="{TemplateBinding Content}" 
                        Foreground="White"
                         HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                        VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                </Border>

ボタン テンプレートの作成にこのコードを挿入する必要がありますが、ボタン コンテンツにテキストを配置するときにこれを使用し、他のコントロールを追加する場合は通常のスタイルを使用します

于 2015-11-20T20:58:01.583 に答える