2016-08-02 80 views
3

我想学习WPF自己,这是一个斗争。我需要知道如何通过绑定来设置附加属性的值。附加属性Grid.Row和Grid.RowSpan是目的地用于绑定,而不是源。 StackOverflow已经提出了类似的问题,但是他们被真正认识WPF的人问及回答,或者涉及价值转换器等复杂问题。我还没有找到适用于我的答案。如何绑定附加属性

在这种情况下,我有一个表示一整天的时间表的网格,并且我想向它添加事件。每个事件将从特定的网格行开始,并跨越多行,具体取决于事件的开始时间和持续时间。我的理解是,你必须使用依赖项属性作为绑定的源,所以我的事件对象ActivityBlock具有StartIncrementDurationIncrements依赖项属性来描述它在网格上的位置。

就是这样,这就是我想要做的:通过绑定在网格中创建一个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> 

下面是代码隐藏文件的主窗口:

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

我对您要实现的内容有模糊的线索。但是你很难将CodeBehind和xaml混合在一起,以至于无法实现。问问你自己一件事。我会在CodeBehind中执行**所有操作**,并且能够访问我希望的每一个控件,或者我应该切换到MVVM,因为DataBinding仅在此处有意义。 – lokusking

回答

1

它可以绑定Grid.Row这里是下面MVVM一个简单的例子,

查看

<Window x:Class="WpfApplication3.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 
<Grid> 
    <Grid.RowDefinitions> 
     <RowDefinition Height="20"/> 
     <RowDefinition Height="20"/> 
     <RowDefinition Height="20"/> 
    </Grid.RowDefinitions> 
    <TextBlock Text="Hello" Grid.Row="{Binding RowNumber}"></TextBlock> 
</Grid> 
</Window> 

视图模型

public class ViewModel 
{ 
    public ViewModel() 
    { 
     RowNumber = 2; 
    } 

    public int RowNumber { get; set; } 
} 

我已将DataContext设置为从CodeBehind查看。如下所示,其中DataContext链接到ViewModel类。我们可以用不同的方式设置DataContext

xaml.cs

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
     DataContext = new ViewModel();  
    } 
} 

当你启动应用程序时,将设置RowNumber两个,并显示在第三排的TextBlock。稍后,您可以通过更新RowNumber来更改该行ViewModel

+0

谢谢阿宾。您的示例视图非常清晰,但我对ViewModel有疑问。在你的例子中,什么将ViewModel链接到TextBlock?如果我在代码中创建第二个TextBlock并将其添加为子项,那么如何将其链接到新的ViewModel,以便每个TextBlock将显示在网格上的正确空间中? –

+0

我编辑了答案,以显示如何从代码隐藏设置dataContext。添加更多文本块就像添加一个新的文本块到TextBlocks集合中,这样您就可以添加任何容器来容纳它。您已经在问题中使用了ItemsControl,因此它的集合可以根据绑定到它的项目源的集合的数量来增加其中的控件或元素。如果需要在ItemsControl中设置属性,则需要使用样式。希望它有帮助。谷歌是你最好的朋友。 –