2016-08-02 25 views
3

Ich versuche, WPF selbst zu lernen, und es ist ein bisschen ein Kampf. Ich muss wissen, wie man den Wert einer angehängten Eigenschaft durch das Binden einstellt. Die angehängten Eigenschaften Grid.Row und Grid.RowSpan sind das Ziel für die Bindung, nicht die Quelle. Ähnliche Fragen wurden zu StackOverflow gestellt, aber sie wurden von Leuten gestellt und beantwortet, die WPF wirklich kennen, oder sie beinhalten Komplikationen wie Wertwandler. Ich habe keine Antwort gefunden, die für mich anwendbar und nachvollziehbar ist.Wie angeheftete Eigenschaft zu binden ist

In diesem Fall habe ich ein Raster, das einen ganzen Tag Zeitplan darstellt, und ich möchte Ereignisse hinzufügen. Jedes Ereignis beginnt in einer bestimmten Rasterzeile und umfasst eine Reihe von Zeilen, abhängig von der Startzeit und Dauer des Ereignisses. Mein Verständnis ist, dass Sie Abhängigkeitseigenschaften als Quelle für Bindungen verwenden müssen, so dass mein Ereignisobjekt ActivityBlock die StartIncrement und DurationIncrements Abhängigkeitseigenschaften hat, um seine Position im Raster zu beschreiben.

Das ist es, das ist alles was ich tun möchte: Machen Sie ein UserControl Position sich in einem Gitter über Bindungen.

Ich glaube mein Problem ist am wahrscheinlichsten in meinem MainWindow XAML, die Bindungen auf dem Gitter falsch einzurichten. (Beachten Sie, dass ich die Rasterzeilen programmgesteuert im Konstruktor erstelle, also suchen Sie nicht im XAML unten nach ihnen).

Wenn ich meine Anwendung ausführen, wird das Ereignis erstellt, aber es wird nicht an der richtigen Stelle im Raster angezeigt, als ob Grid.Row und Grid.RowSpan nie aktualisiert werden. Ist Grid.Row und Grid.RowSpan nicht bindbar? Wenn nicht, was mache ich falsch?

Hier ist MainWindow.xaml, wo mein Problem ist am wahrscheinlichsten sein:

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

Hier ist der Code-behind ist Datei für das Hauptfenster:

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; 

    } 
} 

Hier ist der ActivityBlock Code:

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; 

    } 
} 

Die entsprechende XAML ist nicht interessant, aber ich schließe es, falls es benötigt wird:

<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

aktualisieren Ich habe eine vage Ahnung, was Sie erreichen möchten. Aber Sie mischen CodeBehind mit XAML so schwer, dass es unmöglich ist, es zu erreichen. Fragen Sie sich eine Sache. Will ich ** alles ** in CodeBehind tun und in der Lage sein, auf jedes Steuerelement zuzugreifen, das ich möchte, oder sollte ich zu MVVM wechseln, da DataBinding dort nur Sinn macht. – lokusking

Antwort

1

Es ist möglich, Grid.Row hier zu binden, ist ein einfaches Beispiel unten mit MVVM,

Ansicht

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

Ansichtsmodell

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

    public int RowNumber { get; set; } 
} 

Ich habe den DataContext auf View von CodeBehind gesetzt. wie unten, welche DataContext ist mit ViewModel Klasse verbunden. Wir können DataContext auf verschiedene Arten einstellen.

xaml.cs

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

Wenn Sie die Anwendung starten, wird es die RowNumber zwei und zeigen die TextBlock auf der 3. Reihe gesetzt.später können Sie die Zeile ändern, indem Sie RowNumber von ViewModel

+0

Danke, Abin. Ihre Beispielansicht ist sehr klar, aber ich habe Fragen zum ViewModel. In Ihrem Beispiel, was verbindet ViewModel mit dem TextBlock? Wenn ich einen zweiten TextBlock im Code anlege und ihn als Kind hinzufüge, wie verknüpfe ich ihn dann mit einem neuen ViewModel, so dass jeder TextBlock im richtigen Bereich des Rasters angezeigt wird? –

+0

Ich habe die Antwort bearbeitet, um zu zeigen, wie man DataContext von Codebehind setzt. Das Hinzufügen von mehr Textblock ist wie das Hinzufügen eines neuen Textblocks zu einer Sammlung von TextBlocks, so dass Sie einen Container hinzufügen können, um ihn zu halten. Sie haben ItemsControl in der Frage verwendet, so dass es eine Sammlung ist, die die Steuerelemente oder Elemente darin basierend auf der Nummer der Sammlung, die an die Itemsource gebunden ist, wachsen lässt. Sie müssen Styles verwenden, wenn Sie Eigenschaften in ItemsControl festlegen müssen. hoffe es hilft. und Google ist dein bester Freund. –