时间预订视觉显示

时间:2013-09-24 10:57:31

标签: c# .net winforms visual-studio-2012

我有一个管理车辆和员工的系统,当您根据日期点击他们的名字时,您应该能够看到他们当天可用的时间。

根据上一张表格中选择的日期,它只会显示1天!所以我需要1列,但时间可能是12:30-14:15等等。

像这样的视觉效果:

视觉时间视觉时间

照片:

enter image description here

我已经研究过创建一个自定义控件或用户控件但我对这个主题的了解很少,而且我已经花了几个小时在一个圆圈里跑来跑去。

3 个答案:

答案 0 :(得分:4)

发布此答案是因为OP要求:

这是你在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>

代码背后:

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

        DataContext = new TimeBookingsViewModel();
    }
}

视图模型:

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) });
    }
}

数据项:

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);
    }
}

还有一些助手(转换器等):

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();
    }
}
  • 使用MVVMDataBindingThe WPF Mentality
  • ,用户界面与数据和逻辑分开
  • 通过处理您自己的类和属性,以及单独使用UI,这可以使您的代码几乎为空,并且您的应用程序代码非常干净。
  • 没有“所有者绘制”,没有P / Invoke(无论这意味着什么),没有复杂的大小/位置计算,也没有蹩脚的程序“绘图代码”。只有漂亮的声明性XAML和DataBinding才能实现简单,简单的属性。
  • 使用2 ItemsControls创建用户界面DataTemplates(一个用于“背景”小时框,另一个用于预订视觉表示)
  • “预订”文本块位于Viewbox内,使其延伸到可用大小。如果你愿意,你可以改变它,但我无法想象一种更好的方法来使文本适合不同预订的可用空间。
  • 我甚至花时间添加了很好的描述性ToolTip。您可以在WPF中really do what you want
  • 我强烈建议你阅读这篇文章中的所有链接材料,主要是Rachel的“WPF Mentality”和相关的博客文章。如果您需要进一步的帮助,请告诉我。

底线:

忘记winforms,它太有限了,它没有(真正的)数据绑定,它需要大量的代码来做更少的事情,它不支持任何级别的自定义,它迫使你创建糟糕的Windows 95喜欢用户界面。

WPF Rocks :只需将我的代码复制并粘贴到File -> New Project -> WPF Application中,然后自行查看结果。

答案 1 :(得分:1)

评估开发控件所花费的时间,将其乘以您的成本/小时,添加一些您将(肯定)产生的错误,并将其与一些经过验证的测试解决方案进行比较:

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

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

我建议您购买您的控件(或某些)。

答案 2 :(得分:1)

您最好的选择是创建一个继承自类似控件的自定义控件,对于您的示例,沿着图片框的行可能是有益的。 对于(略微过时的C ++)自定义控件,请参阅:http://msdn.microsoft.com/en-us/library/ms364048(v=vs.80).aspx

就自定义绘图设置而言:http://msdn.microsoft.com/en-us/library/windows/desktop/bb761817(v=vs.85).aspx

作为概括,这个想法如下: 捕获WM_PAINT事件,并在该事件中向控件渲染预先绘制的图像(这通常通过创建绘制表面,然后将其复制到控件的可渲染疼痛区域来完成)此方法避免任何'闪烁”。

绘图命令大多是简单的'drawline(xy_start,xy_end)。

最后,为了处理一天中的时间,如果你选择(rendersurface.height /(24 * 60)),你将有一个时间到像素的转换。例如:

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现在是白天该时间的像素y坐标。