Does my code demonstrate good WPF practice?
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 < 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;
}
}
}
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.
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.
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.
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>
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
You can follow the MVVM pattern if you later decide to use binding. For more details:
http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
You can use the 'RelayCommand' to go to the next day if you want to show the appointments separately for each day.
链接地址: http://www.djcxy.com/p/44654.html上一篇: WPF更改ViewModel.PropertyChanged事件上的按钮内容
下一篇: 我的代码是否表现出良好的WPF实践?