我在WPF中有一个画布,包含在一个视图框中,用于将其约束为方形宽高比:
<Viewbox Stretch="Uniform">
<!-- make it square!-->
<Canvas x:Name="canvas" DataContextChanged="canvas_DataContextChanged" Width="300" Height="300"/>
</Viewbox>
现在,当我在代码中的画布中放置标签时,我发现字体大小很大,即使我将其设置得更小,也不会缩小。哎呀,如果我收缩视图本身,文本就会被切断并变得不可见!这是我放置标签的代码(以及一些显示我游戏中扇区实际内容的“扇区视图”控件):
var w = canvas.ActualWidth / StarSystem.Diameter;
var h = canvas.ActualHeight / StarSystem.Diameter;
// TODO - place background
foreach (var sector in StarSystem.Sectors)
{
// place sector view
var sectorView = new SectorView { Sector = sector, Width = w, Height = h };
canvas.Children.Add(sectorView);
Canvas.SetLeft(sectorView, (sector.X + StarSystem.Radius) * w);
Canvas.SetTop(sectorView, (sector.Y + StarSystem.Radius) * h);
Canvas.SetZIndex(sectorView, 0);
// place label
if (sector.SpaceObjects.Any())
{
var label = new Label { Content = sector.LargestSpaceObject.Name, VerticalAlignment= VerticalAlignment.Bottom, HorizontalAlignment = HorizontalAlignment.Center, Height=12, FontSize=10};
canvas.Children.Add(label);
Canvas.SetLeft(label, (sector.X + StarSystem.Radius) * w - label.ActualWidth / 2);
Canvas.SetTop(label, (sector.Y + StarSystem.Radius) * h);
Canvas.SetZIndex(label, 1);
}
}
就目前而言,标签是不可见的(我假设因为文本被切断了),但是如果我删除字体大小和高度属性,那么字母就像扇区视图一样大,这也是大 - 我整个屏幕上只有15x15网格;我希望文本最多可能溢出一个或两个扇区,沿着扇区的最底部,而不是覆盖整个空间! ;)
答案 0 :(得分:0)
问题在于您明确指定了高度。一个FontSize
为10的Label控件需要额外的像素用于实际行间距,并且它通常在顶部和底部都有一个默认的5像素边距,所有这些都将导致一个高约23像素的控件。你给你的标签VerticalAlignment
VerticalAlignment.Bottom
(将文本本身推到底部),然后明确地将高度设置为12,这足以完全切断底部。这里的解决方案是没有明确指定高度,但如果由于某种原因你必须给它一个VerticalAlignment.Top
的对齐,请使高度足够大以容纳所有内容并删除标签填充。
但是HighCore是完全正确的,在代码隐藏中创建你的控件,就像这是对WPF的可怕滥用。
更新:
WPF是完美的工具。正如HighCore所说,我们的想法是在视图模型中执行所有操作,这样您就可以确实放置断点等并准确跟踪正在发生的事情。这是一个实现类似于我认为你想做的事情的例子......
我没有你的数据结构,所以我首先要自己创建一对。我将在这里偷工减料并将所有数据放在视图模型中,在真实世界的MVVM中,您可以将数据保存在模型中,然后使用视图模型公开数据,但让事情变得简单:
public class StarSystem : ViewModelBase
{
private ObservableCollection<Sector> _Sectors;
public ObservableCollection<Sector> Sectors
{
get { return this._Sectors; }
set { this._Sectors = value; RaisePropertyChanged(); }
}
private double _Diameter;
public double Diameter
{
get { return this._Diameter; }
set { this._Diameter = value; RaisePropertyChanged(); }
}
}
public class Sector : ViewModelBase
{
private string _Name;
public string Name
{
get { return this._Name; }
set { this._Name = value; RaisePropertyChanged(); }
}
private double _X;
public double X
{
get { return this._X;}
set { this._X = value; RaisePropertyChanged(); }
}
private double _Y;
public double Y
{
get { return this._Y;}
set { this._Y = value; RaisePropertyChanged(); }
}
private double _Size;
public double Size
{
get { return this._Size; }
set { this._Size = value; RaisePropertyChanged(); }
}
现在在我的主窗口中,我将创建一个星系并将其设置为我的DataContext:
public MainWindow()
{
InitializeComponent();
this.DataContext = new StarSystem
{
Diameter = 300,
Sectors = new ObservableCollection<Sector>{
new Sector{Name="Sector 1", X=50, Y=50, Size=110},
new Sector{Name="Sector 2", X=160, Y=190, Size=90},
new Sector{Name="Sector 3", X=225, Y=70, Size=70},
}
};
}
这里没什么特别新的或聪明的,它只是你要显示的对象的原始数据,包括位置和大小等。这是绘制它的XAML:
<Viewbox Margin="20">
<ItemsControl ItemsSource="{Binding Sectors}" Width="{Binding Diameter}" Height="{Binding Diameter}" Background="CornflowerBlue">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Ellipse Fill="AliceBlue"/>
<TextBlock Text="{Binding Name}" FontSize="12" Foreground="Black" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding X}" />
<Setter Property="Canvas.Top" Value="{Binding Y}" />
<Setter Property="Width" Value="{Binding Size}" />
<Setter Property="Height" Value="{Binding Size}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Viewbox>
请注意,我已经将Viewbox保留在那里,我假设你希望整个场景随着窗口的大小调整而缩放,所以我们会保留它。这里有一些事情:
ItemsControl
。ItemsPanelTemplate
中并将其设置为ItemsControl.ItemsPanel
。ItemsControl.ItemTemplate
指定DataTemplate
,其中包含我们要用于呈现每个扇区的控件。DataTemplate
中执行,但ItemControl实际上将DataTemplate
包裹在ContentPresenter
中,因此我们需要设置它。我们通过在ItemsControl.ItemContainerStyle
。结果:
我花了大约2分钟来编写代码......比编写这个解释花费的时间少得多!您现在拥有的数据结构是视图的逻辑表示。您可以随意添加和删除扇区。您可以更改它们的位置和大小,它将反映在它们的渲染中。您可以对它们进行单元测试并运行模拟,而无需实际创建视图。如果为GUI事件添加处理程序,则可以添加断点并查看确切的内容。在所有这些中,任何地方都没有一行代码隐藏,你甚至不必使用你在原始代码中创建的自定义“SectorView”控件。