开发者

WPF Tips for graphically displaying a sequence on/off durations

开发者 https://www.devze.com 2023-02-02 21:57 出处:网络
I\'m working on an application where users can select between multiple sequences of on/off durations. Sequences always start with the on period and can have a varying length (but always in on/off pair

I'm working on an application where users can select between multiple sequences of on/off durations. Sequences always start with the on period and can have a varying length (but always in on/off pairs): e.g.

var sequences = new []
{
    new int[] { 10, 15 },        // 10 ms on, 15 ms off
    new int[] { 15, 10, 5, 10 }  // 15 ms on, 10 ms off, 5 ms on, 10 ms off
};

The sequences have a maximum duration of 10 seconds and will be repeated. One special sequence defines no on/off durations: it is always on (though I might be able to change that to {1,0} or something).

Instead of displaying the numbers on screen I'd like to show a little graphical representation for the f开发者_运维百科ull 10 second duration (repeating shorter sequences) so the user can compare patterns. These will be displayed in a combo box that resizes with the window. For the examples above it would look something like the following (where X is a filled in background)

xx   xx   xx   xx   xx   xx   xx...
xxx  x  xxx  x  xxx  x  xxx  x  ...

I suppose I'll have to use a value converter (if only for the special value), but am uncertain what the best/easiest way of creating the graphs is, especially with the resize requirement and repeating the shorter sequences. A canvas, something else?

I'd greatly appreciate any tips!


I would follow this basic approach:

  1. Write a value converter that takes each sequence and repeats the sequence into the full 10 seconds, encoding each chunk of time with a class that specifies whether the period is 'On' and the duration.

  2. For each sequence, bind to the ItemsSource of an ItemsControl. For the ItemsPanel, use a StackPanel with Horizontal orientation. For the ItemTemplate, use a Rectangle or whatever other visual you'd like for a chunk of time, with the Width bound to the duration. You've also included a handy 'IsOn' property now so that you can easily visualize the On/Off state. Don't worry about scaling the Width at this point.

  3. Place the ItemsControl in a ViewBox, which can be allowed to stretch to its parent container. Now you have a visual that provides the correct proportions of duration and scales with size.


Here's a bare-bones implementation (no error handling or any attempt to make it pretty):

UDPATE: Fixed a bug that didn't properly truncate repeating sequence at 10 seconds.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace TestWpf
{
    public class SeqSegment
    {
        public bool IsOn { get; set; }
        public int Duration { get; set; }
    }

    public class SeqConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var result = new List<SeqSegment>();
            var seq = (int[]) value;
            int time = 0;
            int i = 0;
            bool isOn = true;
            while (time < 10000)
            {
                result.Add(new SeqSegment { Duration = Math.Min(seq[i], 10000 - time), IsOn = isOn });
                isOn = !isOn;
                time += seq[i];
                i++;
                if (i >= seq.Length)
                    i = 0;
            }

            return result;
        }

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

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public IEnumerable<int[]> TestSequences
        {
            get
            {
                yield return new[] {10, 5000, 10, 8};
                yield return new[] {500, 5000};
                yield return new[] {50, 400, 30, 10};
            }
        }

        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
        }
    }
}

XAML:

<Window x:Class="TestWpf.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:TestWpf="clr-namespace:TestWpf" Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <TestWpf:SeqConverter x:Key="SeqConverter"/>
        <DataTemplate x:Key="SeqSegTemplate">
            <Rectangle x:Name="Rect" Width="{Binding Duration}" Fill="Blue"/>
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding IsOn}" Value="True">
                    <Setter TargetName="Rect" Property="Fill" Value="Green"/>
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>
        <DataTemplate x:Key="SeqTemplate">
            <Viewbox Height="50" Stretch="Fill">
                <ItemsControl ItemsSource="{Binding Converter={StaticResource SeqConverter}}" ItemTemplate="{StaticResource SeqSegTemplate}">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <StackPanel Orientation="Horizontal" Height="1"/>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                </ItemsControl>
            </Viewbox>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <ItemsControl ItemsSource="{Binding TestSequences}" ItemTemplate="{StaticResource SeqTemplate}"/>
    </Grid>
</Window>
0

精彩评论

暂无评论...
验证码 换一张
取 消