2016-08-02 128 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中設置屬性,則需要使用樣式。希望它有幫助。谷歌是你最好的朋友。 –