开发者

Trouble displaying an object in WPF

I'm so new to this that I can't even phrase the question right...

Anyway, I'm trying to do something very simple and have been unable to figure it out. I have the following class:

public class Day : Control, INotifyPropertyChanged
{
    public static readonly DependencyProperty DateProperty =
        DependencyProperty.Register("Date", typeof(int), typeof(Day));

    public int Date
    {
        get { return (int)GetValue(DateProperty); }
   开发者_运维知识库     set 
        { 
            SetValue(DateProperty, value); 
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs("Date"));
            }
        }
    }

    public static readonly DependencyProperty DayNameProperty =
        DependencyProperty.Register("DayName", typeof(String), typeof(Day));

    public String DayName
    {
        get { return (String)GetValue(DayNameProperty); }
        set 
        { 
            SetValue(DayNameProperty, value); 
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs("DayName"));
            }
        }
    }

    static Day()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(Day), new FrameworkPropertyMetadata(typeof(Day)));
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion
}

I've learned that you can't call a constructor that has parameters in XAML so the only way to actually set some data for this class is through the two properties, DayName and Date.

I created a ControlTemplate for Day which is as follows:

<Style TargetType="{x:Type con:Day}">
    <Setter Property="MinHeight" Value="20"/>
    <Setter Property="MinWidth" Value="80"/>
    <Setter Property="Height" Value="20"/>
    <Setter Property="Width" Value="80"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type con:Day}">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>
                    <Rectangle Grid.ColumnSpan="2" x:Name="rectHasEntry" Fill="WhiteSmoke"/>    
                    <TextBlock Grid.Column="0" x:Name="textBlockDayName" Text="{TemplateBinding DayName}" FontFamily="Junction" FontSize="11" Background="Transparent"
                               HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,2,0,0"/>
                    <TextBlock Grid.Column="1" x:Name="textBlockDate" Text="{TemplateBinding Date}" FontFamily="Junction" FontSize="11" Background="Transparent"
                               HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,2,0,0"/>
                    <Rectangle Grid.ColumnSpan="2" x:Name="rectMouseOver" Fill="#A2C0DA" Opacity="0"
                               Style="{StaticResource DayRectangleMouseOverStyle}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

I then render it on screen in my MainWindow thusly:

<Window x:Class="WPFControlLibrary.TestHarness.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:con="clr-namespace:WPFControlLibrary.Calendar;assembly=WPFControlLibrary"
    Title="MainWindow" Height="500" Width="525"
    WindowStartupLocation="CenterScreen">
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="80"/>
    </Grid.ColumnDefinitions>
    <con:Day Grid.Column="1" Height="20" Width="80" DayName="Mon" Date="1"/>
</Grid>

And what I actually see is, well, nothing. If I put my cursor on the con:Day line of the XAML it'll highlight the correctly sized rectangle in the window but I don't see "Mon" on the left side of the rectangle and "1" on the right.

What am I doing wrong? I suspect it's something simple but I'll be darned if I'm seeing it.

My ultimate goal is to group a bunch of the Day controls within a Month control, which is then contained in a Year control as I'm trying to make a long Calendar Bar that lets you navigate through the months and years, while clicking on a Day would display any information saved on that date. But I can't even get the Day part to display independent of anything else so I'm a long way from the rest of the functionality. Any help would be greatly appreciated.


First of all, you don't need to implement INotifyPropertyChanged if you have DependencyProperties. The following is more than enough:

    public int Date
    {
        get { return (int)GetValue(DateProperty); }
        set { SetValue(DateProperty, value); }
    }

When I try your example (without rectMouseOver, since I don't have the definition of DayRectangleMouseOverStyle), Mon shows just fine but 1 does not show up. I was able to fix that by replacing TemplateBinding with an explicit binding: Instead of

{TemplateBinding Date}

use

{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Date}

After that change, your example worked fine. I don't know why, but TemplateBinding seems to be "broken" sometimes.


You need to setup a DataTemplate for your class, not a ControlTemplate.

DataTemplates are used to define how a custom class is diplayed. ControlTemplates, via Style, is used to stylize a control.

For details, I recommend reading the Data Templating Overview on MSDN.


You're approaching this from an angle that is going to give you nothing but grief. I know this because I did exactly what you're trying to do once.

Consider approaching it this way: You have a Day class that exposes DayName, DayNumber, and Column properties. You can then create a data template for that class:

<DataTemplate DataType="{x:Type local:Day}">
   <TextBlock Grid.Column="{Binding Column}" 
              Text="{Binding DayNumber}" 
              ToolTip="{Binding DayName}"/>
</DataTemplate>

Now create a Week class that contains a collection of Day objects. Create a template for that class:

<DataTemplate DataType="{x:Type local:Week}">
  <ItemsControl ItemSource={Binding Days}>
    <ItemsControl.ItemsPanel>
      <ItemsPanelTemplate>
        <Grid>
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
          </Grid.ColumnDefinitions>
        </Grid>
      </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
  </ItemsControl>
</DataTemplate>

And a Month class that contains a collection of Week objects. Its data template looks like this:

<DataTemplate>
  <ItemsControl ItemsSource="{Binding Weeks}"/>
</DataTemplate>

(You don't need to create an ItemsPanelTemplate here because the template that ItemsPanel uses by default, a vertical StackPanel, is the one you want.

Now if you create an instance of a Month object (and populate its weeks and days correctly), anywhere in your XAML that WPF needs to render it, it will create a vertical StackPanel containing several Grids, each of which contains seven TextBlocks with the appropriate day numbers in them.

Creating a Year object and a template for it I'll leave as an exercise for you. Eventually you'll add ScrollViewers and styling to these templates, and implement more properties in the object model to help with the UI. For instance, if you want a day to display differently if it has information, you might add a HasInformation property to the Day class, and implement a data trigger to change its background color or font weight if it's true. And you'll implement Command objects for the things you actually want this to do, like display the information for a specific day. You'll get there.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜