【问题标题】:Time Bookings Visual Display时间预订视觉显示
【发布时间】:2013-09-24 10:57:31
【问题描述】:

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

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

类似这样的视觉效果:

视觉时间视觉时间

图片:

我曾考虑创建一个自定义控件或用户控件,但我对该主题的了解很少,而且我花了几个小时在一个圈子里跑来跑去。

【问题讨论】:

  • 您有什么具体问题吗?没有人会为你实现这个。你至少可以展示你尝试过的东西。
  • 我正在寻找类似的东西或正确方向的一点,而不是寻找编码它的人!不知道从哪里开始,所以没有任何代码,目前我可以输出一个带有预订时间的文本框,当天开始:结束:等,但在视觉上需要它。任何类似的项目/想法都会有所帮助,我尝试使用网格和着色,但 1/2 或 1/4 小时不起作用。谢谢
  • 这完全取决于您的 SQL 获取数据。你的表单仍然是一样的,一个简单的网格绑定到获取的数据
  • 我可以输出 08:00 到 18:00 的网格,目前的问题是视觉显示预订,例如 13:00-14:50
  • @HighCore 看到你的一些关于将 winforms 转换为 WPF 的帖子,现在这并不容易,我已经编程 vb.net 6 年,c# 2 并且了解 php/html 所以猜测 WPF 可能很容易上手,取决于你上面谈到的那个例子,我可能会在 WPF 中重新编码我的项目

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


【解决方案1】:

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

这就是你在 WPF 中的做法:

<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 才能简单、简单的属性。
  • UI 是使用 2 个 ItemsControls 和不同的 DataTemplates 创建的(一个用于“背景”小时框,另一个用于预订视觉表示)
  • “已预订”文本块位于 Viewbox 内,这使它可以拉伸到可用大小。您可以根据需要更改它,但我想不出更好的方法来使文本适合不同预订的可用空间。
  • 我什至花时间添加了漂亮的描述性ToolTip。您可以在 WPF 中really do what you want
  • 我强烈建议您阅读这篇文章中的所有链接材料,主要是 Rachel 的“WPF Mentality”和相关博客文章。如果您需要进一步的帮助,请告诉我。

底线:

忘记winforms吧,它太有限了,它没有(真正的)数据绑定,它需要很多代码来做更少的事情,它不支持任何级别的自定义,它迫使你创建糟糕的Windows 95 个类似 UI。

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

【讨论】:

  • 所以几个月前我拿起了 WPF 并想,这看起来太复杂了,我太忙于学习,我很惊讶它是如此简单,而且你的代码很棒!谢谢人,救生员:)
  • 快速提问,刚刚尝试从上午 8 点到下午 6 点开始日期时间,所以将范围更改为 (8,11),这给出了开始 8,结束时间下午 6 点,但在视图中时间仍然从上午 1 点开始到凌晨 3:50,当我将鼠标悬停在它上面时,有没有办法改变它?可用 = new ObservableCollection(Enumerable.Range(8, 11).Select(x => new DateTime(2013, 1, 1).AddHours(x))); Bookings = new ObservableCollection();
  • @JamesSmithyCleave 数据来自TimeRanges,他们的构造函数期望int starthour, int startminute, int endhour, int endminute,创建它们时更改这些值。
  • 这是我所做的更改,但仍然遇到问题:Img:i42.tinypic.com/sg06ro.png 看起来预订也需要一个范围,但看不到code public TimeBookingsViewModel() { Available = new ObservableCollection&lt;DateTime&gt;(Enumerable.Range(8, 11).Select(x =&gt; new DateTime(2013, 1, 1).AddHours(x))); Bookings = new ObservableCollection&lt;TimeRange&gt;(); Bookings.Add(new TimeRange(8, 0, 9, 50)); Bookings.Add(new TimeRange(10, 0, 11, 00)); Bookings.Add(new TimeRange(12, 00, 13, 30)); }
  • @jamesSmithyCleave 我基本上将边距转换器更改为现在接受整个 TimeRange 实体并执行一些计算以确定应该基于TimeRange.Base 开始时间和TimeRange.Start 值的Margin 是什么。
【解决方案2】:

评估您将花费在开发控件上的时间,将其乘以您的每小时成本,添加一些您将(肯定)产生的错误,并将其与一些现有的、经过良好测试的解决方案进行比较:

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

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

我建议你购买你的控制(或一些)。

【讨论】:

    【解决方案3】:

    您最好的选择是创建一个继承自类似控件的自定义控件,例如,类似于图片框的某些东西可能是有益的。 有关自定义控件的(稍微过时的 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 坐标。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-18
    • 1970-01-01
    相关资源
    最近更新 更多