Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
257 views
in Technique[技术] by (71.8m points)

c# - Time Bookings Visual Display

I have a system which manages Vehicles and Staff, when you click on their name based on a date you should be able to see the times that they are available on that day.

It will only show 1 day based on the date chosen on the previous form! So I need 1 column but times could be 12:30-14:15 etc

Something visual like this:

Visual Time Visual Time

Picture:

enter image description here

I have looked in to creating a custom control or user control but my knowledge on the subject is low and I've spent a few hours running around in a circle.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Posting this answer because the OP requested it:

This is how you do that in WPF:

enter image description here

<Window x:Class="MiscSamples.TimeBookings"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MiscSamples"
    Title="TimeBookings" Height="300" Width="300">
<Window.Resources>
    <local:TimeRangeToVerticalMarginConverter x:Key="VerticalMarginConverter"/>
    <local:TimeRangeHeightConverter x:Key="HeightConverter"/>
</Window.Resources>

<ScrollViewer>
    <Grid>
        <ItemsControl ItemsSource="{Binding Available}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Border BorderBrush="Black" BorderThickness="1" Height="60">
                        <TextBlock Text="{Binding StringFormat='hh tt'}"
                               HorizontalAlignment="Center" VerticalAlignment="Center"/>
                    </Border>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

        <ItemsControl ItemsSource="{Binding Bookings}">
            <ItemsControl.ItemContainerStyle>
                <Style TargetType="ContentPresenter">
                    <Setter Property="Margin" Value="{Binding Converter={StaticResource VerticalMarginConverter}}"/>
                    <Setter Property="Height" Value="{Binding Converter={StaticResource HeightConverter}}"/>
                    <Setter Property="VerticalAlignment" Value="Top"/>
                </Style>
            </ItemsControl.ItemContainerStyle>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Border Background="#601050FF" BorderBrush="LightSkyBlue" BorderThickness="1"
                        x:Name="Border">
                        <Viewbox Stretch="Uniform">
                            <TextBlock Text="Booked" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="16">
                            <TextBlock.LayoutTransform>
                                <RotateTransform Angle="-45"/>
                            </TextBlock.LayoutTransform>
                            </TextBlock>
                        </Viewbox>
                        <Border.ToolTip>
                            <ToolTip>
                                <StackPanel>
                                    <TextBlock>
                                        <Run Text="From" FontWeight="Bold"/>
                                        <Run Text="{Binding StartString, Mode=OneWay}"/>
                                    </TextBlock>

                                    <TextBlock>
                                        <Run Text="To" FontWeight="Bold"/>
                                        <Run Text="{Binding EndString,Mode=OneWay}"/>
                                    </TextBlock>
                                </StackPanel>
                            </ToolTip>
                        </Border.ToolTip>

                    </Border>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Grid/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </Grid>
</ScrollViewer>

Code Behind:

public partial class TimeBookings : Window
{
    public TimeBookings()
    {
        InitializeComponent();

        DataContext = new TimeBookingsViewModel();
    }
}

ViewModel:

public class TimeBookingsViewModel
{
    public ObservableCollection<DateTime> Available { get; set; } 

    public ObservableCollection<TimeRange> Bookings { get; set; }

    public TimeBookingsViewModel()
    {
        Available = new ObservableCollection<DateTime>(Enumerable.Range(8, 11).Select(x => new DateTime(2013, 1, 1).AddHours(x))); 

        Bookings = new ObservableCollection<TimeRange>(); 

        Bookings.Add(new TimeRange(8, 0, 9, 50) {Base = TimeSpan.FromHours(8)});
        Bookings.Add(new TimeRange(10, 0, 11, 00) { Base = TimeSpan.FromHours(8) });
        Bookings.Add(new TimeRange(12, 00, 13, 30) { Base = TimeSpan.FromHours(8) });
    }
}

Data Item:

public class TimeRange
{
    public TimeSpan Base { get; set; }

    public TimeSpan Start { get; set; }

    public TimeSpan End { get; set; }

    public string StartString { get { return new DateTime(Start.Ticks).ToString("hh:mm tt"); } }

    public string EndString { get { return new DateTime(End.Ticks).ToString("hh:mm tt"); } }

    public TimeRange(int starthour, int startminute, int endhour, int endminute)
    {
        Start = new TimeSpan(0, starthour, startminute, 0);
        End = new TimeSpan(0, endhour, endminute, 0);
    }
}

And a few helpers (Converters and such):

public class TimeRangeToVerticalMarginConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (!(value is TimeRange))
            return null;

        var range = (TimeRange) value;

        return new Thickness(2, range.Start.TotalMinutes - range.Base.TotalMinutes, 2, 0);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

public class TimeRangeHeightConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (!(value is TimeRange))
            return null;

        var range = value as TimeRange;

        return range.End.Subtract(range.Start).TotalMinutes;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
  • The UI is separate from Data and Logic by using MVVM, DataBinding and The WPF Mentality
  • This keeps your code behind almost empty and your application code really clean, by just dealing with your own classes and properties, and leaving the UI alone.
  • No "owner draw", no P/Invoke (whatever that means), no complicated size/position calculations, and no crappy procedural "drawing code". Only beautiful declarative XAML and DataBinding to simple, simple properties.
  • The UI is created by using 2 ItemsControls with different DataTemplates (one for the "background" hour boxes, and the other for the bookings visual representation)
  • The "Booked" textblock is inside a Viewbox which makes it stretch to the available size. You can change that if you want, but I could not imagine a better way to make the text fit the available space for different bookings.
  • I even took the time to add the nice descriptive ToolTip. You can really do what you want in WPF.
  • I strongly suggest you read all the linked material in this post, mostly Rachel's "WPF Mentality" and related blog posts. Let me know if you need further help.

Bottom Line:

Forget winforms, it's too limited, it doesn't have (real) databinding, it requires a lot of code to do less, it does not support any level of customization, and it forces you to create shitty Windows 95 like UIs.

WPF Rocks: Just copy and paste my code in a File -> New Project -> WPF Application and see the results for yourself.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...