Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does my code demonstrate good WPF practice?

Tags:

c#

wpf

I'm starting a WPF project, and just finished the base of the UI, it seems very convoluted though, so I'm not sure if I've gone around laying it out in the right way. I don't want to get to start developing the back-end and realise that I've done the front wrong, and make life harder for myself.

Coming from a background of <DIV>'s and CSS to style this is a lot different, and really want to get it right from the start.

Essentially it's a one week calendar (7 days, Mon-Sunday, defaulting to the current week.) Which will eventually link up to a DB and if I have an appointment for something on this day it will show it in the relevant day.

I've opted for a Grid rather than ListView because of the way it will work I will not be binding the results to a collection or anything along those lines. Rather I will be filling out a Combo box within the canvas for each day (yet to be placed in the code) for each event and on selection it will show me further details.

XAML:

<Window x:Class="WOW_Widget.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:s="clr-namespace:System;assembly=mscorlib"
    xmlns:Extensions="clr-namespace:WOW_Widget"
    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    Title="Window1" Height="239" Width="831">

    <Window.Resources>
        <LinearGradientBrush x:Key="NormalBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Offset="1.0" Color="White"/>
                    <GradientStop Offset="0.0" Color="LightSlateGray"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>
        <LinearGradientBrush x:Key="grdDayHeader" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Offset="0.0" Color="Peru" />
                    <GradientStop Offset="1.0" Color="White" />
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>
        <LinearGradientBrush x:Key="grdToday" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Offset="0.0" Color="LimeGreen"/>
                    <GradientStop Offset="1.0" Color="DarkGreen" />
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>
        <Style TargetType="{x:Type GridViewColumnHeader}">
            <Setter Property="Background" Value="Khaki" />
        </Style>
        <Style x:Key="DayHeader" TargetType="{x:Type Label}">
            <Setter Property="Background" Value="{StaticResource grdDayHeader}" />
            <Setter Property="Width" Value="111" />
            <Setter Property="Height" Value="25" />
            <Setter Property="HorizontalContentAlignment" Value="Center" />
        </Style>
        <Style x:Key="DayField">
            <Setter Property="Canvas.Width" Value="111" />
            <Setter Property="Canvas.Height" Value="60" />
            <Setter Property="Canvas.Background" Value="White" />
        </Style>
        <Style x:Key="Today">
            <Setter Property="Canvas.Background" Value="{StaticResource grdToday}" />
        </Style>
        <Style x:Key="CalendarColSpacer">
            <Setter Property="Canvas.Width" Value="1" />
            <Setter Property="Canvas.Background" Value="Black" />
        </Style>
        <Style x:Key="CalendarRowSpacer">
            <Setter Property="Canvas.Height" Value="1" />
            <Setter Property="Canvas.Background" Value="Black" />
        </Style>
    </Window.Resources>

    <Grid Background="{StaticResource NormalBrush}">
        <Border BorderBrush="Black" BorderThickness="1" Width="785" Height="86" Margin="12,12,12,104">
            <Canvas Height="86" Width="785" VerticalAlignment="Top">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition />
                        <ColumnDefinition />
                        <ColumnDefinition />
                        <ColumnDefinition />
                        <ColumnDefinition />
                        <ColumnDefinition />
                        <ColumnDefinition />
                        <ColumnDefinition />
                        <ColumnDefinition />
                        <ColumnDefinition />
                        <ColumnDefinition />
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition />
                        <RowDefinition />
                        <RowDefinition />
                    </Grid.RowDefinitions>
                    <Label Grid.Column="0" Grid.Row="0" Content="Monday" Style="{StaticResource DayHeader}" />
                    <Canvas Grid.Column="1" Grid.RowSpan="3" Grid.Row="0"  Style="{StaticResource CalendarColSpacer}" />
                    <Label Grid.Column="2" Grid.Row="0" Content="Tuesday" Style="{StaticResource DayHeader}" />
                    <Canvas Grid.Column="3" Grid.RowSpan="3" Grid.Row="0"  Style="{StaticResource CalendarColSpacer}" />
                    <Label Grid.Column="4" Grid.Row="0" Content="Wednesday" Style="{StaticResource DayHeader}" />
                    <Canvas Grid.Column="5" Grid.RowSpan="3" Grid.Row="0"  Style="{StaticResource CalendarColSpacer}" />
                    <Label Grid.Column="6" Grid.Row="0" Content="Thursday" Style="{StaticResource DayHeader}" />
                    <Canvas Grid.Column="7" Grid.RowSpan="3" Grid.Row="0"  Style="{StaticResource CalendarColSpacer}" />
                    <Label Grid.Column="8" Grid.Row="0" Content="Friday" Style="{StaticResource DayHeader}" />
                    <Canvas Grid.Column="9" Grid.RowSpan="3" Grid.Row="0"  Style="{StaticResource CalendarColSpacer}" />
                    <Label Grid.Column="10" Grid.Row="0" Content="Saturday" Style="{StaticResource DayHeader}" />
                    <Canvas Grid.Column="11" Grid.RowSpan="3" Grid.Row="0"  Style="{StaticResource CalendarColSpacer}" />
                    <Label Grid.Column="12" Grid.Row="0" Content="Sunday" Style="{StaticResource DayHeader}" />

                    <Canvas Grid.Column="0" Grid.ColumnSpan="13" Grid.Row="1" Style="{StaticResource CalendarRowSpacer}" />

                    <Canvas Grid.Column="0" Grid.Row="2" Margin="0" Style="{StaticResource DayField}">
                        <Label Name="lblMondayDate" />
                    </Canvas>
                    <Canvas Grid.Column="2" Grid.Row="2" Margin="0" Style="{StaticResource DayField}">
                        <Label Name="lblTuesdayDate" />
                    </Canvas>
                    <Canvas Grid.Column="4" Grid.Row="2" Margin="0" Style="{StaticResource DayField}">
                        <Label Name="lblWednesdayDate" />
                    </Canvas>
                    <Canvas Grid.Column="6" Grid.Row="2" Margin="0" Style="{StaticResource DayField}">
                        <Label Name="lblThursdayDate" />
                    </Canvas>
                    <Canvas Grid.Column="8" Grid.Row="2" Margin="0" Style="{StaticResource DayField}">
                        <Label Name="lblFridayDate" />
                    </Canvas>
                    <Canvas Grid.Column="10" Grid.Row="2" Margin="0" Style="{StaticResource DayField}">
                        <Label Name="lblSaturdayDate" />
                    </Canvas>
                    <Canvas Grid.Column="12" Grid.Row="2" Margin="0" Style="{StaticResource DayField}">
                        <Label Name="lblSundayDate" />
                    </Canvas>
                </Grid>
            </Canvas>
        </Border>
        <Canvas Height="86" HorizontalAlignment="Right" Margin="0,0,12,12" Name="canvas1" VerticalAlignment="Bottom" Width="198"></Canvas>        
    </Grid>
</Window>

CS:

public partial class Window1 : Window {
    private DateTime today = new DateTime();
    private Label[] Dates = new Label[7];
    public Window1() {
        DateTime start = today = DateTime.Now;
        int day = (int)today.DayOfWeek;
        while (day != 1) {
            start = start.Subtract(new TimeSpan(1, 0, 0, 0));
            day--;
        }
        InitializeComponent();
        Dates[0] = lblMondayDate;
        Dates[1] = lblTuesdayDate;
        Dates[2] = lblWednesdayDate;
        Dates[3] = lblThursdayDate;
        Dates[4] = lblFridayDate;
        Dates[5] = lblSaturdayDate;
        Dates[6] = lblSundayDate;
        FillWeek(start);
    }

    private void FillWeek(DateTime start) {
        for (int d = 0; d &lt; Dates.Length; d++) {
            TimeSpan td = new TimeSpan(d, 0, 0, 0);
            DateTime _day = start.Add(td);
            if (_day.Date == today.Date) {
                Canvas dayCanvas = (Canvas)Dates[d].Parent;
                dayCanvas.Style = (Style)this.Resources["Today"];
            }
            Dates[d].Content = (int)start.Add(td).Day;
        }
    }
}
like image 330
Psytronic Avatar asked Mar 12 '10 10:03

Psytronic


2 Answers

I would say, no, you're not going about this the right way. You're doing too much of the work and letting WPF do too little. You should be using data binding and an ItemsControl, and let WPF do all the heavy lifting of figuring out where to put things and what to put in them.

  1. I used an XmlDataProvider in this example because it's the easiest way to demonstrate how data-binding can be used without writing code. You'd probably build a view model class that exposed a collection of objects with Name and Date properties and bind to an instance of that.

  2. Using Canvas and pixel-by-pixel layouts is a Bad Thing in WPF, which is all about building resolution-independent UIs. It's much better to make the layout engine do all that work.

  3. Not everything that looks like a grid needs to be laid out with the Grid. There's much, much less XAML if you lay this grid out as a horizontal StackPanel. This keeps you from having to screw around with row and column numbers. The only reason my layout uses a Grid is to make the border size to its content.

Here's my rework of what you provided. To my eye the result looks pretty much identical to yours, but it's about half as much XAML (not counting the brushes) and it's way easier to modify.

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Background="{DynamicResource NormalBrush}">

  <Page.Resources>
       <XmlDataProvider x:Key="Days" XPath="Days">
        <x:XData>
          <Days xmlns="">
            <Day Name="Sunday" Date="03/14/2010"/>
            <Day Name="Monday" Date="03/15/2010"/>
            <Day Name="Tuesday" Date="03/16/2010"/>
            <Day Name="Wednesday" Date="03/17/2010"/>
            <Day Name="Thursday" Date="03/18/2010"/>
            <Day Name="Friday" Date="03/19/2010"/>
            <Day Name="Saturday" Date="03/20/2010"/>
          </Days>
        </x:XData>
       </XmlDataProvider>

        <LinearGradientBrush x:Key="NormalBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Offset="1.0" Color="White"/>
                    <GradientStop Offset="0.0" Color="LightSlateGray"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <LinearGradientBrush x:Key="DayHeaderBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Offset="0.0" Color="Peru" />
                    <GradientStop Offset="1.0" Color="White" />
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>        

  </Page.Resources>
        <Grid Margin="50">
          <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>            
          </Grid.RowDefinitions>
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
          </Grid.ColumnDefinitions>
          <StackPanel Orientation="Horizontal">
            <ItemsControl ItemsSource="{Binding Source={StaticResource Days}, XPath=Day}">
              <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                  <StackPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
              </ItemsControl.ItemsPanel>
              <ItemsControl.ItemTemplate>
                <DataTemplate>
                <StackPanel Orientation="Vertical" Background="White">
                    <Border BorderBrush="Black" BorderThickness="1,1,0,0" Background="{StaticResource DayHeaderBrush}">
                      <TextBlock Margin="30,10" HorizontalAlignment="Center" Text="{Binding XPath=@Name}"/>
                    </Border>
                    <Border BorderBrush="Black" BorderThickness="1,1,0,0">
                      <TextBlock Margin="30,10" Height="50" HorizontalAlignment="Center" Text="{Binding XPath=@Date}"/>
                    </Border>
                  </StackPanel>
                </DataTemplate>
              </ItemsControl.ItemTemplate>
            </ItemsControl>
            <Border BorderBrush="Black" BorderThickness="0,0,1,0"/>
          </StackPanel>
        </Grid>
</Page>
like image 187
Robert Rossney Avatar answered Sep 18 '22 15:09

Robert Rossney


I don't know how big your project is going to be, but the biggest tip I can give you when starting WPF is to look into the PRISM framework:

http://www.codeplex.com/CompositeWPF

http://msdn.microsoft.com/en-us/magazine/cc785479.aspx

It's an excellent framework, and solves a lot of problems for you.

Regarding your calendar, I would recommend making it a re-usable look-less control, this article may help you:

http://www.codeproject.com/KB/WPF/WPFOutlookCalendar.aspx

like image 22
David Masters Avatar answered Sep 17 '22 15:09

David Masters