C# 是否有方法缩放视口,使其内容在控件中显示的所有视口中保持一致?

C# 是否有方法缩放视口,使其内容在控件中显示的所有视口中保持一致?,c#,wpf,scaling,viewbox,C#,Wpf,Scaling,Viewbox,我使用ViewBox控件和TextBlock控件来显示我使用的应用程序中存在的大多数文本 我遇到的问题是,在大多数情况下,效果不是很好。有些文本可能比其他文本大得多,这并不理想。我也不能只在所有文本中指定字体,因为我不知道用户的硬件,而ViewBox似乎是理想的解决方案,除了这个问题 我创建了一个Behavior类来管理这个问题,它在大多数情况下都工作得很好。。。除了现在。这让我相信我的方法并不理想,我希望有人能给我指出一个更“正确”的方向 我正在使用ItemsControl显示一些信息,这就是

我使用ViewBox控件和TextBlock控件来显示我使用的应用程序中存在的大多数文本

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

我创建了一个
Behavior
类来管理这个问题,它在大多数情况下都工作得很好。。。除了现在。这让我相信我的方法并不理想,我希望有人能给我指出一个更“正确”的方向

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

符合必要的“要求”

这是窗口代码-

<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>

当我运行应用程序时,文本在我只能假设是某种循环(或重复调用
LayoutUpdated
)中来回移动。所以,正如我所说的,如果有其他更可靠的方法来知道什么时候有孩子加入(或移除)一个小组,这将使这变得更容易。这是一个大问题,但我必须在这里列出所有内容。

首先,我对所有文本使用ViewBox是错误的方法。在每个文本元素中具有不同“字体大小”的应用程序看起来都很乏味。最好在应用程序启动时选择合适的布局和固定的总字体大小。@Clemens问题是,当控件占用整个窗口(其大小不是静态的)时,使用静态字体大小并不好。我不能只说“好的,字体大小12。那很好”,因为如果窗口只被几个文本块占用(情况就是这样),那么就会有一卡车的死区。不理想。你不能把整个控件放在一个视图框中吗?无论如何,我不会将文本元素增长到荒谬的大尺寸,我会选择不同的布局,并且不会让控件占据整个窗口。@Clemens
将整个控件放在一个Viewbox
-这可能正是我需要的。我试试看。但我读到的所有东西都说“使用视口”。可能是我做得不对。我将尝试将ItemControl放在一个viewbox中。首先,对所有文本使用viewbox是错误的方法。在每个文本元素中具有不同“字体大小”的应用程序看起来都很乏味。最好在应用程序启动时选择合适的布局和固定的总字体大小。@Clemens问题是,当控件占用整个窗口(其大小不是静态的)时,使用静态字体大小并不好。我不能只说“好的,字体大小12。那很好”,因为如果窗口只被几个文本块占用(情况就是这样),那么就会有一卡车的死区。不理想。你不能把整个控件放在一个视图框中吗?无论如何,我不会将文本元素增长到荒谬的大尺寸,我会选择不同的布局,并且不会让控件占据整个窗口。@Clemens
将整个控件放在一个Viewbox
-这可能正是我需要的。我试试看。但我读到的所有东西都说“使用视口”。可能是我做得不对。我将尝试将ItemControl放在一个viewbox中。
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;
        }
    }
}
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; }
        }
    }
}