TemplateBinding в ControlTemplate в стиле CustomControl [дубликат]

Обычно мы не вызываем функцию сразу после ее записи в программе. В чрезвычайно простых терминах, когда вы вызываете функцию сразу после ее создания, она называется IIFE - причудливое имя.

24
задан 2 February 2010 в 20:20
поделиться

5 ответов

У меня были похожие ситуации в ControlTemplate s, где я хотел привязать атрибут «To» к значению (а не жестко-кодировать его), и наконец нашел решение.

Краткое примечание: если вы копаетесь в Интернете, вы найдете примеры людей, которые могут использовать привязку данных для свойств «От» или «Кому». Однако в этих примерах раскадровки не находятся в стиле или контрольной таблице . Если ваш Storyboard находится в стиле или ControlTemplate, вам придется использовать другой подход, например, это решение.

Это решение распространяется вокруг проблемы с замораживанием, поскольку оно просто оживляет двойное значение от 0 до 1 Он работает с умным использованием свойства Tag и Multiply-конвертера. Вы используете многосвязывание для привязки как к желаемому свойству, так и к вашему «масштабу» (тегу), которые умножаются вместе. В основном идея заключается в том, что значение вашего тега - это то, что вы оживляете, и его значение действует как «масштаб» (от 0 до 1), приведя значение «желаемого» значения к «полной шкале» после того, как вы активировали тег на 1.

Вы можете увидеть это в действии здесь . Суть этого заключается в следующем:

<local:MultiplyConverter x:Key="multiplyConverter" />
<ControlTemplate x:Key="RevealExpanderTemp" TargetType="{x:Type Expander}">
    <!-- (other stuff here...) -->
    <ScrollViewer x:Name="ExpanderContentScrollView">
        <!-- ** BEGIN IMPORTANT PART #1 ...  -->
        <ScrollViewer.Tag>
            <sys:Double>0.0</sys:Double>
        </ScrollViewer.Tag>
        <ScrollViewer.Height>
            <MultiBinding Converter="{StaticResource multiplyConverter}">
               <Binding Path="ActualHeight" ElementName="ExpanderContent"/>
               <Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
            </MultiBinding>
        </ScrollViewer.Height>
        <!-- ...end important part #1.  -->
        <ContentPresenter x:Name="ExpanderContent" ContentSource="Content"/>

    </ScrollViewer>

  <ControlTemplate.Triggers>
    <Trigger Property="IsExpanded" Value="True">
        <Trigger.EnterActions>
            <BeginStoryboard>
                <Storyboard>
                   <!-- ** BEGIN IMPORTANT PART #2 (make TargetProperty 'Tag') ...  -->
                   <DoubleAnimation Storyboard.TargetName="ExpanderContentScrollView"
                         Storyboard.TargetProperty="Tag"
                         To="1"
                         Duration="0:0:0.4"/>
                    <!-- ...end important part #2 -->
               </Storyboard>
            </BeginStoryboard>
        </Trigger.EnterActions>
    </Trigger>
  </ControlTemplate.Triggers>
</ControlTemplate>

С помощью этого преобразователя значений:

public class MultiplyConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
       double result = 1.0;
       for (int i = 0; i < values.Length; i++)
       {
           if (values[i] is double)
               result *= (double)values[i];
       }

       return result;
    }

   public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
   {
       throw new Exception("Not implemented");
   }
}
47
ответ дан 0xBADF00D 22 August 2018 в 21:46
поделиться
  • 1
    Как это сделать, но привязать к высоте в моей модели? – claudekennilol 22 October 2015 в 18:04
  • 2
    Не могли бы вы объяснить мне, почему это отличается от простой привязки? – Fᴀʀʜᴀɴ Aɴᴀᴍ 10 November 2015 в 11:44
  • 3
    @ Джейсон Фрэнк Я нашел хороший способ победить заморозку, а также проверить мое решение – patrick 28 July 2016 в 21:03

Насколько я знаю, вы не можете привязать анимацию к / из, потому что анимация должна быть заморожена.

8
ответ дан David Cater 22 August 2018 в 21:46
поделиться

Мне нравится решение Джейсона Фрэнка. Однако это еще проще и менее подвержено ошибкам, если вы не используете тег, но вместо этого, например, свойство Width пустого фиктивного элемента Border. Это родное двойное свойство, поэтому нет необходимости в синтаксисе <sys:Double>, и вы можете называть границу так же, как и с такой переменной:

<!-- THIS IS JUST THE SLIDING AMIMATION MATH -->
<!-- animated Border.Width    From 0 to 1 -->
<Border x:Name="Var_Animation_0to1" Width="0"/>
<!-- animated Border.Width    From 0 to (TotalWidth-SliderWidth) -->
<Border x:Name="Var_Slide_Length">
    <Border.Width>
        <MultiBinding Converter="{mvvm:MathConverter}" ConverterParameter="a * (b-c)">
            <Binding ElementName="Var_Animation_0to1" Path="Width"/>
            <Binding ElementName="BackBorder" Path="ActualWidth"/>
            <Binding ElementName="Slider" Path="ActualWidth"/>
        </MultiBinding>
    </Border.Width>
</Border>

Это делает привязки более читаемыми.

Анимация всегда 0..1, как указал Джейсон:

<BeginStoryboard Name="checkedSB">
    <Storyboard Storyboard.TargetProperty="Width" Storyboard.TargetName="Var_Animation_0to1">
        <DoubleAnimation To="1" Duration="00:00:00.2"/>
    </Storyboard>
</BeginStoryboard>

Затем привяжите все, что вы хотите оживить, к ширине фиктивной границы. Таким образом, вы можете даже конвертировать цепи друг в друга так:

<Border x:Name="Slider" HorizontalAlignment="Left"
        Margin="{Binding ElementName=Var_Slide_Length, Path=Width, Converter={StaticResource DoubleToThickness}, ConverterParameter=x 0 0 0}"/>

В сочетании с MathConverter вы можете делать почти что угодно в стилях: https://www.codeproject.com/Articles/ 239251 / MathConverter-How-To-Do-Math-в-XAML

1
ответ дан JCH2k 22 August 2018 в 21:46
поделиться

Я выполнил эту точную вещь.

<UserControl x:Class="YOURNAMESPACE.UserControls.SliderControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:YOURNAMESPACE.UserControls"
             xmlns:converter="clr-namespace:YOURNAMESPACE.ValueConverters"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             SizeChanged="UserControl_SizeChanged">
    <UserControl.Resources>

        <converter:MathConverter x:Key="mathConverter" />

        <LinearGradientBrush x:Key="CheckedBlue" StartPoint="0,0" EndPoint="0,1">
            <GradientStop Color="#e4f5fc" Offset="0" />
            <GradientStop Color="#e4f5fc" Offset="0.1" />
            <GradientStop Color="#e4f5fc" Offset="0.1" />
            <GradientStop Color="#9fd8ef" Offset="0.5" />
            <GradientStop Color="#9fd8ef" Offset="0.5" />
            <GradientStop Color="#bfe8f9" Offset="1" />
        </LinearGradientBrush>
        <LinearGradientBrush x:Key="CheckedOrange" StartPoint="0,0" EndPoint="0,1">
            <GradientStop Color="#FFCA6A13" Offset="0" />
            <GradientStop Color="#FFF67D0C" Offset="0.1" />
            <GradientStop Color="#FFFE7F0C" Offset="0.1" />
            <GradientStop Color="#FFFA8E12" Offset="0.5" />
            <GradientStop Color="#FFFF981D" Offset="0.5" />
            <GradientStop Color="#FFFCBC5A" Offset="1" />
        </LinearGradientBrush>

        <SolidColorBrush x:Key="CheckedOrangeBorder" Color="#FF8E4A1B" />
        <SolidColorBrush x:Key="CheckedBlueBorder" Color="#FF143874" />

        <Style x:Key="CheckBoxSlider" TargetType="{x:Type CheckBox}">
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}" />
            <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" />

            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type CheckBox}" >

                        <DockPanel x:Name="dockPanel" 
                               Width="{TemplateBinding ActualWidth}" 
                               Height="{TemplateBinding Height}" >
                            <DockPanel.Resources>

                                <Storyboard x:Key="ShowRightStoryboard">
                                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="slider" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)">
                                        <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0" />
                                    </DoubleAnimationUsingKeyFrames>
                                </Storyboard>

                                <Storyboard x:Key="ShowLeftStoryboard" >
                                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" 
                                                           Storyboard.TargetName="slider" 
                                                           Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)"
                                                           >
                                        <SplineDoubleKeyFrame x:Name="RightHalfKeyFrame" KeyTime="00:00:00.1000000" Value="300" />
                                    </DoubleAnimationUsingKeyFrames>
                                </Storyboard>
                            </DockPanel.Resources>

                            <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                                          Content="{TemplateBinding Content}" 
                                          ContentStringFormat="{TemplateBinding ContentStringFormat}" 
                                          ContentTemplate="{TemplateBinding ContentTemplate}" 
                                          RecognizesAccessKey="True" 
                                          VerticalAlignment="Center" />
                            <Grid>

                                <Border x:Name="BackgroundBorder" BorderBrush="#FF939393" BorderThickness="1" CornerRadius="3" 

                                Width="{TemplateBinding ActualWidth}" 
                                Height="{TemplateBinding Height}" >

                                    <Border.Background>
                                        <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                                            <GradientStop Color="#FFB5B5B5" Offset="0" />
                                            <GradientStop Color="#FFDEDEDE" Offset="0.1" />
                                            <GradientStop Color="#FFEEEEEE" Offset="0.5" />
                                            <GradientStop Color="#FFFAFAFA" Offset="0.5" />
                                            <GradientStop Color="#FFFEFEFE" Offset="1" />
                                        </LinearGradientBrush>
                                    </Border.Background>
                                    <Grid>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition />
                                            <ColumnDefinition />
                                        </Grid.ColumnDefinitions>
                                        <TextBlock x:Name="LeftTextBlock"  Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:SliderControl}}, Path=LeftText, Mode=TwoWay}" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center"  />
                                        <TextBlock x:Name="RightTextBlock"  Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:SliderControl}}, Path=RightText, Mode=TwoWay}" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center"  />
                                    </Grid>
                                </Border>

                                <Border x:Name="slider" 
                                    BorderBrush="#FF939393" 
                                    HorizontalAlignment="Left" 
                                    Width="{TemplateBinding ActualWidth, Converter={StaticResource mathConverter}, ConverterParameter=/2}" 
                                    Height="{TemplateBinding Height}"
                                    BorderThickness="1" 
                                    CornerRadius="3" 
                                    RenderTransformOrigin="0.5,0.5" Margin="0"
                                    >
                                    <Border.RenderTransform>
                                        <TransformGroup>
                                            <ScaleTransform ScaleX="1" ScaleY="1" />
                                            <SkewTransform AngleX="0" AngleY="0" />
                                            <RotateTransform Angle="0" />
                                            <TranslateTransform X="{TemplateBinding ActualWidth, Converter={StaticResource mathConverter}, ConverterParameter=/2}" Y="0" />
                                        </TransformGroup>
                                    </Border.RenderTransform>
                                    <Border.Background>
                                        <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
                                            <GradientStop Color="#FFF0F0F0" Offset="0" />
                                            <GradientStop Color="#FFCDCDCD" Offset="0.1" />
                                            <GradientStop Color="#FFFBFBFB" Offset="1" />
                                        </LinearGradientBrush>
                                    </Border.Background>
                                    <DockPanel Background="Transparent" LastChildFill="False">
                                        <Viewbox x:Name="SlideRight" Stretch="Uniform" Width="28" Height="28" DockPanel.Dock="Right" Margin="0,0,50,0" >
                                            <Path  Stretch="Fill"  Fill="{DynamicResource TextBrush}">
                                                <Path.Data>
                                                    <PathGeometry Figures="m 27.773437 48.874779 -8.818359 9.902343 -4.833984 0 8.847656 -9.902343 -8.847656 -10.019532 4.833984 0 z m -11.396484 0 -8.7597655 9.902343 -4.9804687 0 9.0234372 -9.902343 -9.0234372 -10.019532 4.9804687 0 z" FillRule="NonZero"/>
                                                </Path.Data>
                                            </Path>
                                        </Viewbox>
                                        <Viewbox x:Name="SlideLeft" Stretch="Uniform" Width="28" Height="28" DockPanel.Dock="Left" Margin="50,0,0,0" >
                                            <Path  Stretch="Fill"  Fill="{DynamicResource TextBrush}">
                                                <Path.LayoutTransform>
                                                    <TransformGroup>
                                                        <ScaleTransform ScaleX="-1"/>
                                                    </TransformGroup>
                                                </Path.LayoutTransform>
                                                <Path.Data>
                                                    <PathGeometry Figures="m 27.773437 48.874779 -8.818359 9.902343 -4.833984 0 8.847656 -9.902343 -8.847656 -10.019532 4.833984 0 z m -11.396484 0 -8.7597655 9.902343 -4.9804687 0 9.0234372 -9.902343 -9.0234372 -10.019532 4.9804687 0 z" FillRule="NonZero"/>
                                                </Path.Data>
                                            </Path>
                                        </Viewbox>
                                    </DockPanel>
                                </Border>
                            </Grid>
                        </DockPanel>

                        <ControlTemplate.Triggers>
                            <Trigger Property="IsChecked" Value="True">
                                <Setter TargetName="BackgroundBorder" Property="Background" Value="{StaticResource CheckedOrange}" />
                                <Setter TargetName="BackgroundBorder" Property="BorderBrush" Value="{StaticResource CheckedOrangeBorder}" />
                                <Setter TargetName="SlideRight" Property="Visibility" Value="Collapsed" />
                                <Setter TargetName="SlideLeft" Property="Visibility" Value="Visible" />
                            </Trigger>
                            <Trigger Property="IsChecked" Value="False">
                                <Setter TargetName="BackgroundBorder" Property="Background" Value="{StaticResource CheckedBlue}" />
                                <Setter TargetName="BackgroundBorder" Property="BorderBrush" Value="{StaticResource CheckedBlueBorder}" />
                                <Setter TargetName="SlideRight" Property="Visibility" Value="Visible" />
                                <Setter TargetName="SlideLeft" Property="Visibility" Value="Collapsed" />
                            </Trigger>
                        </ControlTemplate.Triggers>

                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>


    </UserControl.Resources>

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition  Width="*"/>
        </Grid.ColumnDefinitions>

        <CheckBox   x:Name="checkBox" 
                    Style="{StaticResource CheckBoxSlider}" 
                    HorizontalAlignment="Stretch"
                    DockPanel.Dock="Top" 
                    Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:SliderControl}}, Path=Height, Mode=TwoWay}"
                    IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:SliderControl}}, Path=IsLeftVisible, Mode=TwoWay}"
                    Checked="CheckBox_Checked" 
                    Unchecked="CheckBox_Unchecked"
                    />
    </Grid>
</UserControl>

code behind.

namespace YOURNAMESPACE.UserControls
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;

    /// <summary>
    /// Interaction logic for SliderControl.xaml
    /// </summary>
    public partial class SliderControl : UserControl
    {
        public static readonly DependencyProperty IsLeftVisibleProperty =
         DependencyProperty.RegisterAttached(
             "IsLeftVisible",
             typeof(bool),
             typeof(SliderControl),
             new UIPropertyMetadata(true, IsLeftVisibleChanged));

        public static readonly DependencyProperty LeftTextProperty =
            DependencyProperty.RegisterAttached(
                "LeftText",
                typeof(string),
                typeof(SliderControl),
                new UIPropertyMetadata(null, LeftTextChanged));

        public static readonly DependencyProperty RightTextProperty =
        DependencyProperty.RegisterAttached(
            "RightText",
            typeof(string),
            typeof(SliderControl),
            new UIPropertyMetadata(null, RightTextChanged));

         /// <summary>
        /// Initializes a new instance of the <see cref="SliderControl"/> class.
        /// </summary>
        public SliderControl()
        {
            this.InitializeComponent();
        }

        public string LeftText { get; set; }

        public string RightText { get; set; }

        [AttachedPropertyBrowsableForType(typeof(SliderControl))]
        public static bool GetIsLeftVisible(SliderControl sliderControl)
        {
            return (bool)sliderControl.GetValue(IsLeftVisibleProperty);
        }

        [AttachedPropertyBrowsableForType(typeof(SliderControl))]
        public static string GetLeftText(SliderControl sliderControl)
        {
            return (string)sliderControl.GetValue(LeftTextProperty);
        }

        public static void SetIsLeftVisible(SliderControl sliderControl, bool value)
        {
            sliderControl.SetValue(IsLeftVisibleProperty, value);
        }

        public static void IsLeftVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SliderControl slider = d as SliderControl;

            if ((bool)e.NewValue == true)
            {
                slider.RunAnimation("ShowLeftStoryboard");
            }
            else
            {
                slider.RunAnimation("ShowRightStoryboard");
            }
        }

        [AttachedPropertyBrowsableForType(typeof(SliderControl))]
        public static string GetRightText(SliderControl sliderControl)
        {
            return (string)sliderControl.GetValue(RightTextProperty);
        }

        public static void SetLeftText(SliderControl sliderControl, string value)
        {
            sliderControl.SetValue(LeftTextProperty, value);
        }

        public static void SetRightText(SliderControl sliderControl, string value)
        {
            sliderControl.SetValue(RightTextProperty, value);
        }

        private static void LeftTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SliderControl slider = d as SliderControl;
            slider.LeftText = e.NewValue as string;
        }

        private static void RightTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            SliderControl slider = d as SliderControl;
            slider.RightText = e.NewValue as string;
        }

        private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            this.checkBox.Width = e.NewSize.Width;

            CheckBox cb = this.checkBox;
            var controlTemplate = cb.Template;

            DockPanel dockPanel = controlTemplate.FindName("dockPanel", cb) as DockPanel;
            Storyboard story = dockPanel.Resources["ShowLeftStoryboard"] as Storyboard;

            DoubleAnimationUsingKeyFrames dk = story.Children[0] as DoubleAnimationUsingKeyFrames;
            SplineDoubleKeyFrame sk = dk.KeyFrames[0] as SplineDoubleKeyFrame;

            // must manipulate this in code behind, binding does not work, 
            // also it cannot be inside of a control template 
            // because storyboards in control templates become frozen and cannot be modified
            sk.Value = cb.Width / 2;

            if (cb.IsChecked == true)
            {
                story.Begin(cb, cb.Template);
            }
        }

        private void CheckBox_Checked(object sender, RoutedEventArgs e)
        {
            this.RunAnimation("ShowLeftStoryboard");
        }

        private void CheckBox_Unchecked(object sender, RoutedEventArgs e)
        {
            this.RunAnimation("ShowRightStoryboard");
        }

        private void RunAnimation(string storyboard)
        {
            CheckBox cb = this.checkBox;
            var controlTemplate = cb.Template;

            DockPanel dockPanel = controlTemplate.FindName("dockPanel", cb) as DockPanel;

            if (dockPanel != null)
            {
                Storyboard story = dockPanel.Resources[storyboard] as Storyboard;

                DoubleAnimationUsingKeyFrames dk = story.Children[0] as DoubleAnimationUsingKeyFrames;
                SplineDoubleKeyFrame sk = dk.KeyFrames[0] as SplineDoubleKeyFrame;
                story.Begin(cb, cb.Template);
            }
        }
    }
}

IValueConverter

namespace YOURNAMESPACE.ValueConverters
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Globalization;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Data;
      /// <summary>
        /// Does basic math operations, eg. value = 60 parameter = "*2 + 1", result = 121
        /// </summary>
        /// <seealso cref="System.Windows.Data.IValueConverter" />
        [ValueConversion(typeof(double), typeof(double))]
        public class MathConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                double d = (double)value;
                string mathExpression = d.ToString() + parameter;

                if (mathExpression.Contains("^"))
                {
                    throw new Exception("Doesn't handle powers or square roots");
                }

                DataTable table = new DataTable();
                table.Columns.Add("expression", typeof(string), mathExpression);
                DataRow row = table.NewRow();
                table.Rows.Add(row);
                double ret = double.Parse((string)row["expression"]);

                if (ret <= 0)
                {
                    return 1d;
                }

                return ret;
            }

            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                // not implemented
                return null;
            }
        }
}

пример использования:

<chart:SliderControl x:Name="sliderControl" 
                                                         LeftText="{Binding Path=LeftTextProperty}"
                                                         RightText="{Binding Path=RightTextProperty}"
                                                         Grid.Row="0" 
                                                         IsLeftVisible="{Binding Path=IsLeftVisible, Mode=TwoWay}" 
                                                         Height="35"  />
1
ответ дан patrick 22 August 2018 в 21:46
поделиться

См. эту ссылку для нескольких других способов решения этой проблемы: http://blogs.microsoft.co.il/blogs/tamir/archive/2007/03/19/How-to-bind-to- Анимация-To-и-ОТ-properties.aspx

1
ответ дан Stephen Hewlett 22 August 2018 в 21:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: