放大ListView内容而不缩放滚动条

时间:2015-06-17 10:08:25

标签: c# wpf xaml scaletransform

我在GridView中有一个ListView。我想在内容中添加 Ctrl + MWheelUp 缩放。

我使用ScaleTransform使用下面的代码实现了缩放部分,但是,由于这会作为整体应用于ListView,因此它也会缩放滚动条。理想情况下,我希望滚动条保持固定大小(虽然显然适应内部内容的变化) - 但是,我不确定如何实现这一点。唯一的方法是将ScaleTransform应用于每个GridViewColumn的每个孩子,或者是否可以使用另一种方法将其应用于ListView作为一个整体,而不是缩放滚动条?

我的(简化)xaml:

<ListView ScrollViewer.HorizontalScrollBarVisibility="Auto"
          ScrollViewer.VerticalScrollBarVisibility="Auto"
          x:Name="listView">

    <ListView.View>
        <GridView>
            <GridViewColumn>...</GridViewColumn>
            <GridViewColumn>...</GridViewColumn>
        </GridView>
    </ListView.View>
</ListView>

xaml.cs:

public Control()
{
    InitializeComponent();
    var mouseWheelZoom = new MouseWheelZoom(listView);
    PreviewMouseWheel += mouseWheelZoom.Zoom;
}

MouseWheelZoom

public class MouseWheelZoom
{
    private readonly FrameworkElement _element;
    private double _currentZoomFactor;

    public MouseWheelZoom(FrameworkElement element)
    {
        _element = element;
        _currentZoomFactor = 1.0;
    }

    public void Zoom(object sender, MouseWheelEventArgs e)
    {
        var handle = (Keyboard.Modifiers & ModifierKeys.Control) > 0;
        if (!handle)
            return;

        ApplyZoom(e.Delta);
    }

    private void ApplyZoom(int delta)
    {
        var zoomScale = delta / 500.0;
        var newZoomFactor = _currentZoomFactor += zoomScale;
        _element.LayoutTransform = new ScaleTransform(newZoomFactor, newZoomFactor);
        _currentZoomFactor = newZoomFactor;
    }
}

3 个答案:

答案 0 :(得分:2)

我刚刚创建了一个附加属性,似乎可以产生您正在寻找的效果。这是该物业的代码。

internal static class ListViewBehaviors
{
    public static readonly DependencyProperty ContentTransformProperty = DependencyProperty.RegisterAttached("ContentTransform", typeof(Transform), typeof(ListViewBehaviors),
        new PropertyMetadata((Transform)null, OnContentTransformChanged));

    public static Transform GetContentTransform(ListView obj)
    {
        return (Transform)obj.GetValue(ContentTransformProperty);
    }

    public static void SetContentTransform(ListView obj, Transform value)
    {
        obj.SetValue(ContentTransformProperty, value);
    }

    private static void OnContentTransformChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        ListView view = obj as ListView;
        if (view != null)
        {
            if (view.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
            {
                EventHandler handler = null;
                handler = (s, a) => ListView_ItemContainerGenerator_StatusChanged(view, handler);
                view.ItemContainerGenerator.StatusChanged += handler;
            }
            else
            {
                UpdateTransform(view);
            }
        }
    }

    private static void ListView_ItemContainerGenerator_StatusChanged(ListView view, EventHandler handler)
    {
        if (view.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
        {
            view.ItemContainerGenerator.StatusChanged -= handler;
            UpdateTransform(view);
        }
    }

    private static void UpdateTransform(ListView view)
    {
        if (view.IsArrangeValid)
        {
            DoUpdateTransform(view);
        }
        else
        {
            EventHandler handler = null;
            handler = (s, e) => LayoutUpdated(view, handler);
            view.LayoutUpdated += handler;
        }
    }

    private static void LayoutUpdated(ListView view, EventHandler handler)
    {
        view.LayoutUpdated -= handler;
        DoUpdateTransform(view);
    }

    private static void DoUpdateTransform(ListView view)
    {
        ScrollViewer scroller = VisualTreeUtility.FindDescendant<ScrollViewer>(view);
        if (scroller != null)
        {
            Transform transform = GetContentTransform(view);

            FrameworkElement header = VisualTreeUtility.FindDescendant<ScrollViewer>(scroller);
            if (header != null)
            {
                header.LayoutTransform = transform;
            }

            FrameworkElement content = scroller.Template.FindName("PART_ScrollContentPresenter", scroller) as FrameworkElement;
            if (content != null)
            {
                content.LayoutTransform = transform;
            }
        }
    }
}

此外,这是VisualTreeUtility.FindDescendant方法的代码。

public static class VisualTreeUtility
{
    public static T FindDescendant<T>(DependencyObject ancestor) where T : DependencyObject
    {
        return FindDescendant<T>(ancestor, item => true);
    }

    public static T FindDescendant<T>(DependencyObject ancestor, Predicate<T> predicate) where T : DependencyObject
    {
        return FindDescendant(typeof(T), ancestor, item => predicate((T)item)) as T;
    }

    public static DependencyObject FindDescendant(Type itemType, DependencyObject ancestor, Predicate<DependencyObject> predicate)
    {
        if (itemType == null) throw new ArgumentNullException("itemType");
        if (ancestor == null) throw new ArgumentNullException("ancestor");
        if (predicate == null) throw new ArgumentNullException("predicate");
        if (!typeof(DependencyObject).IsAssignableFrom(itemType)) throw new ArgumentException("itemType", "The passed in type must be or extend DependencyObject");

        Queue<DependencyObject> queue = new Queue<DependencyObject>();
        queue.Enqueue(ancestor);

        while (queue.Count > 0)
        {
            DependencyObject currentChild = queue.Dequeue();
            if (currentChild != ancestor && itemType.IsAssignableFrom(currentChild.GetType()))
            {
                if(predicate.Invoke(currentChild))
                {
                    return currentChild;
                }
            }

            int count = VisualTreeHelper.GetChildrenCount(currentChild);
            for (int i = 0; i < count; ++i)
            {
                queue.Enqueue(VisualTreeHelper.GetChild(currentChild, i));
            }
        }

        return null;
    }
}

以下是您可以使用该属性的方法:

<ListView
    ItemsSource="{Binding Items}">
    <local:ListViewBehaviors.ContentTransform>
        <ScaleTransform ScaleX="2" ScaleY="2" />
    </local:ListViewBehaviors.ContentTransform>
    <ListView.View>
        ...
    </ListView.View>
</ListView>

如果有任何遗漏或混淆,请告诉我。

答案 1 :(得分:1)

您可以使用随时随地FindChild<T> function来检索ScrollContentPresenter内的ListView,并使用缩放功能。

public Control()
{
    InitializeComponent();
    this.Loaded += new RoutedEventHandler(Control_Loaded);
}

private void Control_Loaded(object sender, RoutedEventArgs e)
{
    var presenter = FindChild<ScrollContentPresenter>(listView, null);
    var mouseWheelZoom = new MouseWheelZoom(presenter);
    PreviewMouseWheel += mouseWheelZoom.Zoom;
}

请注意,我已将代码放在Loaded事件处理程序中。这是因为ScrollContentPresenterListView模板的一部分而不是视图的直接部分,因此在控件完全加载其样式和模板之前它不会存在。

PD。:另外值得注意的是,ListView的其他部分(如标题等)不会被缩放。只有项目会。

答案 2 :(得分:1)

没试过,但你可以尝试做类似

的事情
<ListBox.ItemsPanel>
    <ItemsPanelTemplate>
      <StackPanel /> <!-- scale this, it's inside ScrollViewer -->
    </ItemsPanelTemplate>
</ItemsControl.ItemsPanel>