我使用带有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何时将子项添加(或删除)到(或从中),这将使这更容易。这是一个大问题,但我必须把所有东西都放在这里。