Time Bookings Visual Display

前端 未结 3 1714
有刺的猬
有刺的猬 2020-12-19 18:20

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.

相关标签:
3条回答
  • 2020-12-19 18:45

    Your best bet is to create a custom control inheriting from a similar control, for your example something along the lines of a picture-box may be beneficial. For a (slightly outdated C++) walkthough of custom controls see: http://msdn.microsoft.com/en-us/library/ms364048(v=vs.80).aspx

    As far as custom-drawing setup goes: http://msdn.microsoft.com/en-us/library/windows/desktop/bb761817(v=vs.85).aspx

    As a generalization, the idea is as follows: Capture the WM_PAINT event, and in that event render a pre-drawn image to the control (this is most often done by creating a paint surface, then copying that into the render-able pain-area of the control) this method avoids any 'flicker'.

    The drawing commands are mostly simple, 'drawline(xy_start, xy_end).

    Finally, for handling the time of day, if you take the (rendersurface.height / (24*60)) you will have a converstion from time to pixels. eg:

    double convert_Size = (rendersurface.height / (24*60)); //height / Hours_in_day * Minites
    int time = (hour * 60) + minite_past_hour;
    Pixels_from_top = time * convert_Size;
    

    Pixels_from_top is now the pixel-y coordinate of that time during the day.

    0 讨论(0)
  • 2020-12-19 18:56

    Evaluate the time you will spend to develop your control, multiply this by your cost/hour, add some bug you will (for sure) produce, and compare this to some existant, well tested solutions:

    http://www.telerik.com/products/winforms/scheduler.aspx

    http://www.devexpress.com/Products/NET/Controls/WinForms/Scheduler/

    I suggest you to buy your control (or some).

    0 讨论(0)
  • 2020-12-19 18:57

    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.

    0 讨论(0)
提交回复
热议问题