【发布时间】:2016-08-02 15:13:08
【问题描述】:
我正在尝试自己学习 WPF,这有点困难。我需要知道如何通过绑定设置附加属性的值。附加属性 Grid.Row 和 Grid.RowSpan 是绑定的目标,而不是源。 StackOverflow 上也有人问过类似的问题,但这些问题是由真正了解 WPF 的人提出并回答的,或者它们涉及诸如值转换器之类的复杂问题。我还没有找到对我适用且可以理解的答案。
在这种情况下,我有一个表示一整天日程的网格,我想向其中添加事件。每个事件将从特定的网格行开始并跨越多个行,具体取决于事件的开始时间和持续时间。我的理解是你必须使用依赖属性作为绑定的来源,所以我的事件对象ActivityBlock 具有StartIncrement 和DurationIncrements 依赖属性来描述它在网格上的位置。
就是这样,这就是我想要做的:通过绑定在网格内创建一个 UserControl 位置。
我相信我的问题很可能出在我的 MainWindow XAML 中,在网格上设置的绑定不正确。 (请注意,我在构造函数中以编程方式创建网格行,因此不要在下面的 XAML 中查找它们。
当我运行我的应用程序时,会创建事件,但它没有显示在网格上的正确位置,就好像 Grid.Row 和 Grid.RowSpan 永远不会更新一样。绑定 Grid.Row 和 Grid.RowSpan 是不可能的吗?如果没有,我做错了什么?
这里是 MainWindow.xaml,我的问题很可能是:
<Window x:Class="DayCalendar.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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:DayCalendar"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" Activated="Window_Activated"
>
<DockPanel LastChildFill="True">
<Rectangle Width="50" DockPanel.Dock="Left"/>
<Rectangle Width="50" DockPanel.Dock="Right"/>
<Grid x:Name="TheDay" Background="#EEE">
<ItemsControl ItemsSource="{Binding Path=Children}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Grid.Row" Value="{Binding Path=StartIncrement}" />
<Setter Property="Grid.RowSpan" Value="{Binding Path=DurationIncrements}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Grid>
</DockPanel>
</Window>
这是 MainWindow 的代码隐藏文件:
using System;
using System.Windows;
using System.Windows.Controls;
using DayCalendar.MyControls;
namespace DayCalendar {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
var rowDefs = TheDay.RowDefinitions;
rowDefs.Clear();
for ( int ix = 0; ix < ( 60 / ActivityBlock.MINUTES_PER_INCREMENT ) * 24; ix += 1 ) {
rowDefs.Add( new RowDefinition() );
}
}
private void Window_Activated( object sender, EventArgs e ) {
if ( m_firstActivation ) {
m_firstActivation = false;
var firstActivity = new ActivityBlock();
var today = DateTime.Now.Date;
var startTime = today.AddHours( 11.5 );
var durationSpan = new TimeSpan( 1, 0, 0 );
firstActivity.SetTimeSpan( startTime, durationSpan );
TheDay.Children.Add( firstActivity );
}
}
private bool m_firstActivation = true;
}
}
这里是 ActivityBlock 代码:
using System;
using System.Windows;
using System.Windows.Controls;
namespace DayCalendar.MyControls {
public partial class ActivityBlock : UserControl {
public int StartIncrement {
get { return ( int ) GetValue( StartIncrementProperty ); }
set { SetValue( StartIncrementProperty, value ); }
}
public int DurationIncrements {
get { return ( int ) GetValue( DurationIncrementsProperty ); }
set { SetValue( DurationIncrementsProperty, value ); }
}
public ActivityBlock() {
InitializeComponent();
}
public void SetTimeSpan( DateTime startTime, TimeSpan duration ) {
int startMinute = startTime.Hour * 60 + startTime.Minute;
int durationMinutes = ( int ) duration.TotalMinutes;
StartIncrement = startMinute / MINUTES_PER_INCREMENT;
DurationIncrements = Math.Max( 1, durationMinutes / MINUTES_PER_INCREMENT );
}
static ActivityBlock() {
var thisType = typeof( ActivityBlock );
var affectsArrangeAndMeasure = FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure;
int startIncrementDefault = 0;
StartIncrementProperty = DependencyProperty.Register(
nameof( StartIncrement ),
startIncrementDefault.GetType(),
thisType,
new FrameworkPropertyMetadata( startIncrementDefault, affectsArrangeAndMeasure )
);
int durationIncrementsDefault = 1;
DurationIncrementsProperty = DependencyProperty.Register(
nameof( DurationIncrements ),
durationIncrementsDefault.GetType(),
thisType,
new FrameworkPropertyMetadata( startIncrementDefault, affectsArrangeAndMeasure )
);
}
public const int MINUTES_PER_INCREMENT = 6; // 1/10th of an hour
static public readonly DependencyProperty StartIncrementProperty;
static public readonly DependencyProperty DurationIncrementsProperty;
}
}
相应的 XAML 并不有趣,但我将其包括在内以备不时之需:
<UserControl x:Class="DayCalendar.MyControls.ActivityBlock"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DayCalendar.MyControls"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Border BorderThickness="3" BorderBrush="Black" Background="Chartreuse">
<DockPanel LastChildFill="True">
<TextBlock TextWrapping="WrapWithOverflow">Event Description</TextBlock>
</DockPanel>
</Border>
</UserControl>
【问题讨论】:
-
我对您想要实现的目标有一个模糊的线索。但是你很难将 CodeBehind 与 xaml 混合,这是不可能实现的。问自己一件事。我是否会在 CodeBehind 中执行所有操作并能够访问我希望的每个控件,或者我应该切换到 MVVM,因为 DataBinding 仅在此处有意义。
标签: c# wpf xaml data-binding