有没有办法扩展ViewBoxes,使其显示的内容在控件中显示的所有视图框中是一致的?

时间:2017-06-20 00:11:23

标签: c# wpf scaling viewbox

我使用带有TextBlock控件的ViewBox控件来显示我工作的应用程序中存在的大部分文本。

我遇到的问题是,在大多数情况下,效果并不好。有些文本可能比其他文本大得多,这并不理想。我不能在所有文本中指定字体,因为我不知道用户的硬件,除了这个问题外,ViewBox似乎是理想的解决方案。

我已经创建了一个Behavior<Panel>类来管理这个问题,而且大部分时间它运行得很好......除了现在。这让我相信我的方法并不理想,而且我希望有人可以指出我更正确的#34;方向。

我使用ItemsControl来显示一些信息,这就是问题所在 -

遵守必要的&#34; Minimal, Complete and Verifiable Example&#34;要求...

这是窗口代码 -

<Window
    x:Class="ViewboxScaling.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    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:ViewboxScaling"
    mc:Ignorable="d" Title="MainWindow" Height="350" Width="525">
    <ItemsControl ItemsSource="{Binding ItemSource, Source={x:Static Application.Current}}">
        <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid Columns="2">
                <i:Interaction.Behaviors>
                    <local:ViewBoxScalingBehavior />
                </i:Interaction.Behaviors>
            </UniformGrid>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Viewbox Margin="5,0" HorizontalAlignment="Left">
                <TextBlock Text="{Binding}" />
            </Viewbox>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    </ItemsControl>
</Window>

这是行为类 -

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Interactivity;
using System.Windows.Media;

namespace ViewboxScaling {
    class ViewBoxScalingBehavior : Behavior<Panel> {
        protected override void OnAttached( ) {
            //Initially, I would only use the Loaded event.
            this.AssociatedObject.Loaded += new RoutedEventHandler(
                ( S, E ) => this._bindViewBoxHeight( ) );
            //However, because of my current use case, I need to know
            //whenever the panel has new children added to it.
            this.AssociatedObject.LayoutUpdated += new EventHandler(
                ( S, E ) => this._bindViewBoxHeight( ) );
            base.OnAttached( );
        }

        private void _bindViewBoxHeight( ) {
            List<Viewbox> boxes =
                FindVisualChildren<Viewbox>(
                    this.AssociatedObject ).Where(
                    vb => vb.ActualHeight > 0 &&
                    vb.Child is FrameworkElement ).ToList( );

            foreach ( Viewbox VB in boxes ) {
                //Clear bindings and re-set values.
                BindingOperations.ClearBinding(
                    VB, FrameworkElement.HeightProperty );
                BindingOperations.ClearBinding(
                    VB, FrameworkElement.WidthProperty );
                VB.Height = double.NaN;
                VB.Width = double.NaN;
            }

            Viewbox
                heightSource = boxes.MinBy( vb => vb.ActualHeight ),
                widthSource = boxes.MinBy( vb => vb.ActualWidth );
            Binding
                heightBinding = new Binding( "ActualHeight" ) { Source = heightSource },
                widthBinding = new Binding( "ActualWidth" ) { Source = widthSource };

            foreach ( Viewbox vb in boxes.Where( box => box != heightSource ) )
                BindingOperations.SetBinding(
                    vb, FrameworkElement.HeightProperty, heightBinding );

            foreach ( Viewbox vb in boxes.Where( box => box != widthSource ) )
                BindingOperations.SetBinding(
                    vb, FrameworkElement.WidthProperty, widthBinding );
        }

        public static List<T> FindVisualChildren<T>( DependencyObject o )
            where T : DependencyObject {
            List<T> Children = new List<T>( );
            for ( int x = 0; x < VisualTreeHelper.GetChildrenCount( o ); x++ ) {
                var o2 = VisualTreeHelper.GetChild( o, x );
                if ( o2 != null ) {
                    if ( o2 is T )
                        Children.Add( ( T )o2 );
                    Children.AddRange( FindVisualChildren<T>( o2 ) );
                }
            }
            return Children;
        }

        public static T FindUpVisualTree<T>( DependencyObject initial ) 
            where T : DependencyObject {
            DependencyObject current = initial;
            while ( current != null && current.GetType( ) != typeof( T ) )
                current = VisualTreeHelper.GetParent( current );
            return current as T;
        }
    }

    static class VBSExtensions {
        public static T MinBy<T, U>( this IEnumerable<T> source, Func<T, U> selector )
            where U : IComparable<U> {
            if ( source == null ) throw new ArgumentNullException( "source" );
            bool first = true;
            T minObj = default(T);
            U minKey = default(U);
            foreach ( var item in source ) {
                if ( first ) {
                    minObj = item;
                    minKey = selector( minObj );
                    first = false;
                } else {
                    U currentKey = selector( item );
                    if ( currentKey.CompareTo( minKey ) < 0 ) {
                        minKey = currentKey;
                        minObj = item;
                    }
                }
            }

            return minObj;
        }
    }
}

扩展程序类中的代码是在Panel中搜索任何ViewBox个对象,以及ViewBox个孩子中的所有Panel个元素及其子级,依此类推等等...... MinBy方法用于按高度或宽度查找最小的ViewBox,因为这些项目将作为每个其他视图框的宽度和高度的来源。 / p>

这是App.xaml的代码:

using System.ComponentModel;
using System.Windows;

namespace ViewboxScaling {
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application, INotifyPropertyChanged {
        public event PropertyChangedEventHandler PropertyChanged;
        private readonly string[] _itemSource = new string[] {
            "Text", "Slightly Longer Text"
        };
        public string[ ] ItemSource {
            get { return this._itemSource; }
        }
    }
}

当我运行应用程序时,文本会四处移动,我只能假设是某种循环(或重复调用LayoutUpdated)。因此,正如我所说 - 如果有一些其他更可靠的方法来了解Panel何时将子项添加(或删除)到(或从中),这将使这更容易。这是一个大问题,但我必须把所有东西都放在这里。

0 个答案:

没有答案