在组合框中滑动文本

时间:2017-07-26 07:58:41

标签: c# wpf combobox

我想在组合框中滑动所选项目的文本(选框文本),如果它的长度大于组合框的宽度。它可以是自动的,也可以是用户将鼠标放在组合框上。问题是我完全不知道如何做到这一点。使用渲染变换(之前的文本块定义)可能会这样做吗?还是用故事板?

这是我需要修改的xaml

<DataGrid.ColumnHeaderStyle>
     <Style TargetType="{x:Type DataGridColumnHeader}">
          <Setter Property="ContentTemplate" >
              <Setter.Value>
                  <DataTemplate DataType="DataGridColumnHeader"  >
                      <ComboBox ItemContainerStyle="{StaticResource SingleSelectionComboBoxItem}" DisplayMemberPath="Oggetto" Width="100" Height="20" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},Path=DataContext.Selezione, UpdateSourceTrigger=LostFocus}"  SelectionChanged="SingleSelectionComboBox_SelectionChanged"/>
                  </DataTemplate>
              </Setter.Value>
          </Setter>
      </Style>
</DataGrid.ColumnHeaderStyle>

编辑:问题是我不知道故事板中应该定位哪些属性

EDIT2:我拿了组合框的模板并修改了文本框部分,如下所示:

<Style x:Key="ComboBoxEditableTextBox" TargetType="{x:Type TextBox}">
            <Style.Triggers>
                <EventTrigger RoutedEvent="MouseEnter">
                    <EventTrigger.Actions>
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation From="0" To="100" Duration="00:00:10" Storyboard.TargetProperty="X" Storyboard.TargetName="transferCurreny" RepeatBehavior="Forever"  />
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger.Actions>
                </EventTrigger>
            </Style.Triggers>
            <Setter Property="RenderTransform">
                <Setter.Value>
                    <TranslateTransform x:Name="transferCurreny" X="0"/>
                </Setter.Value>
            </Setter>

问题是这似乎没有效果

编辑3:我意识到我必须使用使用上面提到的风格的模板

            <ControlTemplate x:Key="ComboBoxEditableTemplate" TargetType="{x:Type ComboBox}">
            <Grid x:Name="Placement" SnapsToDevicePixels="true">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
                <Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2"
               IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}"
               PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}"
               Placement="Bottom">
                    <Themes:SystemDropShadowChrome x:Name="Shdw" Color="Transparent"
                                           MaxHeight="{TemplateBinding MaxDropDownHeight}"
                                           MinWidth="{Binding ActualWidth, ElementName=Placement}">
                        <Border x:Name="DropDownBorder"
                        BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}"
                        BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
                            <ScrollViewer x:Name="DropDownScrollViewer">
                                <Grid RenderOptions.ClearTypeHint="Enabled">
                                    <Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
                                        <Rectangle x:Name="OpaqueRect"
                                           Fill="{Binding Background, ElementName=DropDownBorder}"
                                           Height="{Binding ActualHeight, ElementName=DropDownBorder}"
                                           Width="{Binding ActualWidth, ElementName=DropDownBorder}" />
                                    </Canvas>
                                    <ItemsPresenter x:Name="ItemsPresenter"
                                            KeyboardNavigation.DirectionalNavigation="Contained"
                                            SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                                </Grid>
                            </ScrollViewer>
                        </Border>
                    </Themes:SystemDropShadowChrome>
                </Popup>
                <Themes:ListBoxChrome x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}"
                              BorderThickness="{TemplateBinding BorderThickness}"
                              Background="{TemplateBinding Background}" Grid.ColumnSpan="2"
                              RenderMouseOver="{TemplateBinding IsMouseOver}"
                              RenderFocused="{TemplateBinding IsKeyboardFocusWithin}" />
            <TextBox x:Name="PART_EditableTextBox"
                 HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                 IsReadOnly="{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}"
                 Margin="{TemplateBinding Padding}" Style="{StaticResource ComboBoxEditableTextBox}"
                 VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" >
            </TextBox>
                <ToggleButton Grid.Column="1"
                      IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
                      Style="{StaticResource ComboBoxToggleButton}" />
            </Grid>
            <ControlTemplate.Triggers>
                <Trigger Property="IsKeyboardFocusWithin" Value="true">
                    <Setter Property="Foreground" Value="Black" />
                </Trigger>
                <Trigger Property="IsDropDownOpen" Value="true">
                    <Setter Property="RenderFocused" TargetName="Border" Value="true" />
                </Trigger>
                <Trigger Property="HasItems" Value="false">
                    <Setter Property="Height" TargetName="DropDownBorder" Value="95" />
                </Trigger>
                <Trigger Property="IsEnabled" Value="false">
                    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
                    <Setter Property="Background" Value="#FFF4F4F4" />
                </Trigger>
                <MultiTrigger>
                    <MultiTrigger.Conditions>
                        <Condition Property="IsGrouping" Value="true" />
                        <Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false" />
                    </MultiTrigger.Conditions>
                    <Setter Property="ScrollViewer.CanContentScroll" Value="false" />
                </MultiTrigger>
                <Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true">
                    <Setter Property="Margin" TargetName="Shdw" Value="0,0,5,5" />
                    <Setter Property="Color" TargetName="Shdw" Value="#71000000" />
                </Trigger>
                <Trigger Property="ScrollViewer.CanContentScroll" SourceName="DropDownScrollViewer" Value="false">
                    <Setter Property="Canvas.Top" TargetName="OpaqueRect"
                    Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}" />
                    <Setter Property="Canvas.Left" TargetName="OpaqueRect"
                    Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}" />
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>

文本框部分是使用该样式的部分。但是当我在组合框中Template="{StaticResource ComboBoxEditableTemplate}"时,我再也看不到所选项目了,即使它不是空的

3 个答案:

答案 0 :(得分:1)

我一直在研究这个,我想我有你的解决方案。您应该在RenderTransform Storyboard上合并ComboBoxContentPresenter(这是显示当前所选项目的内容)

<ComboBox Grid.Row="1" Name="MyComboBox" Width="200">
        <ComboBox.Resources>
            <Style TargetType="ContentPresenter">
                <Setter Property="RenderTransform">
                    <Setter.Value>
                        <TranslateTransform X="0" Y="0" />
                    </Setter.Value>
                </Setter>
                <Style.Triggers>
                    <EventTrigger RoutedEvent="Window.Loaded">
                        <EventTrigger.Actions>
                            <BeginStoryboard x:Name="ScrollItem">
                                <Storyboard RepeatBehavior="Forever">
                                    <DoubleAnimation Duration="00:00:5" From="0" To="200" Storyboard.TargetProperty="RenderTransform.(TranslateTransform.X)" />
                                    <DoubleAnimation Duration="00:00:5" BeginTime="00:00:5" From="-200" To="0" Storyboard.TargetProperty="RenderTransform.(TranslateTransform.X)" />
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger.Actions>
                    </EventTrigger>
                </Style.Triggers>
            </Style>
        </ComboBox.Resources>
        <ComboBoxItem>
            I am combobox value 1
        </ComboBoxItem>
        <ComboBoxItem>
            I am combobox value 2, Hello!
        </ComboBoxItem>
    </ComboBox>

使用大小为200的ComboBox,从0滚动到200,然后从200滚动到0,将文本从控件的右侧滚动,并在左侧滚动。 (如果您愿意,可以删除第二个DoubleAnimation并将AutoReverse设置为True以使文本在您更喜欢的情况下重新弹回。这不会为您编写太大的项目在控件中,您需要为ComboBox编写一些代码,以便它决定当前选择的Item是否太大,并且从后面的代码(或者可能是自定义ComboBox类)动态打开/关闭故事板。

答案 1 :(得分:1)

08/02更新

基本上为了避免您面临的动画问题 - 您需要在内容网站或选择框ScrollViewer中添加TextBlock作为包装。您可以使用ComboBox的自定义模板选择器执行此操作,如下所示,或者使用ScollViewer提供自定义项目模板。

其次,您可以在C#中创建自定义ComboBox控件以绑定上述模板选择器/项目模板,并在MouseEnterEvent上应用幻灯片动画;或者在XAML中实现它。

对于这个例子 - 已经在C#中实现了控件,并提供了 XAML中的示例用法。

自定义控件和模板选择器的代码

public class SlidingComboBox : ComboBox
{
    public static readonly DependencyProperty SlideForeverProperty = DependencyProperty.Register("SlideForever", typeof(bool), typeof(SlidingComboBox), new FrameworkPropertyMetadata(false));
    public bool SlideForever
    {
        get { return (bool)GetValue(SlideForeverProperty); }
        set { SetValue(SlideForeverProperty, value); }
    }

    protected ContentPresenter _parent;
    protected DoubleAnimation _animation;
    protected TranslateTransform _translate;
    protected Storyboard _storyBoard;

    public SlidingComboBox()
    {
        Loaded += ExComboBox_Loaded;
        ClipToBounds = true;

        //assign template selector - just to re-template ContentSite / selection box 
        //uncomment this code - if you want to default-template for popup-items
        //ItemTemplateSelector = new SlidingComboBoxItemTemplateSelector();        
    }

    private void ExComboBox_Loaded(object sender, RoutedEventArgs e)
    {
        Loaded -= ExComboBox_Loaded;

        //get content-site holder/parent
        _parent = this.GetChildOfType<ContentPresenter>();

        //setup slide animation
        _animation = new DoubleAnimation()
        {
            From = 0,
            RepeatBehavior = SlideForever ? RepeatBehavior.Forever : new RepeatBehavior(1), //repeat only if slide-forever is true
            AutoReverse = SlideForever
        };

        //create storyboard
        _storyBoard = new Storyboard();
        _storyBoard.Children.Add(_animation);
        Storyboard.SetTargetProperty(_animation, new PropertyPath("RenderTransform.(TranslateTransform.X)"));
    }

    protected override void OnMouseEnter(MouseEventArgs e)
    {
        //get actual textblock that renders the selected value
        var textBlock = _parent.GetChildOfType<TextBlock>();
        //and translate-transform for animation
        textBlock.RenderTransform = _translate = new TranslateTransform();

        //start animation only if text-block width is greater than parent
        if (_parent.ActualWidth < textBlock.ActualWidth)
        {
            _animation.Duration = TimeSpan.FromMilliseconds(((int)textBlock.Text?.Length * 100));
            _animation.To = _parent.ActualWidth - textBlock.ActualWidth;
            _storyBoard.Begin(textBlock);
        }

        base.OnMouseEnter(e);
    }

    protected override void OnMouseLeave(MouseEventArgs e)
    {
        //stop animation once mouse pointer is off the control
        _storyBoard.Stop();

        //reset render state
        var textBlock = _parent.GetChildOfType<TextBlock>();
        textBlock.RenderTransform = _translate = new TranslateTransform();

        base.OnMouseLeave(e);
    }

}

public class SlidingComboBoxItemTemplateSelector : DataTemplateSelector
{
    DataTemplate _selectedItemTemplate;

    public SlidingComboBoxItemTemplateSelector()
    {
        //create datatemplate with ScrollViewer and TextBlock as child
        var textBlock = new FrameworkElementFactory(typeof(TextBlock));
        textBlock.SetValue(TextBlock.TextWrappingProperty, TextWrapping.NoWrap);
        textBlock.SetBinding(TextBlock.TextProperty, new Binding("SelectedValue")
        {
            RelativeSource = new RelativeSource { Mode = RelativeSourceMode.FindAncestor, AncestorType = typeof(ComboBox) }
        });
        var scrollViewer = new FrameworkElementFactory(typeof(ScrollViewer));
        scrollViewer.SetValue(ScrollViewer.CanContentScrollProperty, true);
        scrollViewer.SetValue(ScrollViewer.HorizontalScrollBarVisibilityProperty, ScrollBarVisibility.Hidden);
        scrollViewer.SetValue(ScrollViewer.VerticalScrollBarVisibilityProperty, ScrollBarVisibility.Disabled);
        scrollViewer.AppendChild(textBlock);

        _selectedItemTemplate = new DataTemplate
        {
            VisualTree = scrollViewer
        };
    }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        ComboBoxItem comboBoxItem = container.GetVisualParent<ComboBoxItem>();
        if (comboBoxItem == null)
        {
            //send back only if template requested for ContentSite, and not for combo-box item(s)
            return _selectedItemTemplate;
        }
        return null;
    }
}

/// <summary>
/// VisualTree helper
/// </summary>
public static class HelperExtensions
{
    public static T GetChildOfType<T>(this DependencyObject depObj) where T : DependencyObject
    {
        if (depObj == null) return null;

        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
        {
            var child = VisualTreeHelper.GetChild(depObj, i);

            var result = (child as T) ?? GetChildOfType<T>(child);
            if (result != null) return result;
        }
        return null;
    }

    public static T GetVisualParent<T>(this DependencyObject child) where T : Visual
    {
        while ((child != null) && !(child is T))
        {
            child = VisualTreeHelper.GetParent(child);
        }
        return child as T;
    }
}

样本用法:

sample output

<Window x:Class="MarqueeSample.MainWindow"
        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"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MarqueeSample"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        xmlns:col="clr-namespace:System.Collections;assembly=mscorlib"
        mc:Ignorable="d"
        Title="MainWindow" Height="200" Width="400">
    <Grid Margin="20">
        <DataGrid AutoGenerateColumns="False" IsReadOnly="True" Width="225" VerticalAlignment="Center">
            <DataGrid.ItemsSource>
                <col:Hashtable>
                    <col:ArrayList x:Key="TestData1">
                        <sys:String>Tiny</sys:String>
                        <sys:String>Keep calm and love programming</sys:String>
                    </col:ArrayList>
                    <col:ArrayList x:Key="TestData2">
                        <sys:String>Sample string</sys:String>
                        <sys:String>Another string to test</sys:String>
                    </col:ArrayList>
                </col:Hashtable>
            </DataGrid.ItemsSource>
            <DataGrid.Columns>
                <DataGridTextColumn Header="Name" Width="100" Binding="{Binding Key}" />
                <DataGridTemplateColumn Header="Value" Width="100">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <local:SlidingComboBox ItemsSource="{Binding Value}">
                                <local:SlidingComboBox.ItemTemplate>
                                    <DataTemplate>
                                        <ScrollViewer CanContentScroll="True" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Disabled">
                                            <TextBlock Text="{Binding}" />
                                        </ScrollViewer>
                                    </DataTemplate>
                                </local:SlidingComboBox.ItemTemplate>
                            </local:SlidingComboBox>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

您还可以使用SlideForever属性来操作RepeatBehaviour。

<local:SlidingComboBox ItemsSource="{Binding Value}" SlideForever="True" />

答案 2 :(得分:-2)

首先遍历组合框的所有项目,通过将文本分配给标签来检查每个项目的宽度。然后,每次检查宽度,如果当前项目的宽度大于先前项目,则更改最大宽度。

   int DropDownWidth(ComboBox myCombo)
   {
   int maxWidth = 0;
   int temp = 0;
   Label label1 = new Label();

   foreach (var obj in myCombo.Items)
   {
       label1.Text = obj.ToString();
       temp = label1.PreferredWidth;
       if (temp > maxWidth)
       {
           maxWidth = temp;
       }
   }
   label1.Dispose();
   return maxWidth;           
}

private void window_loaded(object sender, EventArgs e)
{
    comboBox1.DropDownWidth = DropDownWidth(comboBox1);
}

OR

int DropDownWidth(ComboBox myCombo)
{
     int maxWidth = 0, temp = 0;
     foreach (var obj in myCombo.Items)
     {
         temp = TextRenderer.MeasureText(obj.ToString(), myCombo.Font).Width;
         if (temp > maxWidth)
         {
             maxWidth = temp;
         }
     }
     return maxWidth;
 }

感谢那些家伙从这里得到它 - &gt; Auto-width of ComboBox's content