1

MVVMベースのWPFアプリケーションでの開発に取り組んでいるタイムラインで小さな障害にぶつかりました。

私の望みは、ビューに表示されるイベントよりも一般的に多くのイベントがあるため、ユーザーが表示したい「ビン」を認識できるようにすることです。これは基本的に、コードビハインドで開発され、ユーザーが選択したビンの上に表示される小さな小さなスタックパネルになります。詳細は以下のとおりです。

基本的な背景は次のとおりです。

  • ビューモデルで発生するイベントをログに記録しています。これらをイベントと呼びます。

  • タイムラインのビューに50x50のキャンバス(イベントごとに1つ)として表示しています。ここでのニュアンスは、スペースが非常に限られているため、表示されるイベントの実際の数をわずかにオフセットされた3つのスタックに制限することです(カードが互いに後ろに積み重ねられ、下にあるカードの上下が表示されていると想像してください)。 )タイムブロックごとに相互に。

  • 時間の各ティック(30秒ごと)で、キャンバスは左に75ピクセルスクロールされ、もちろん、描画されたすべての要素も一緒に移動します。これにより、イベントの「ビン」も設定されます。基本的に、0〜29.9秒はすべてビン0にあり、30〜59.9はビン1などです。

  • バインドされたイベントの表示を担当するItemsControlにリンクされたPreviewMouseLeftButtonDownイベントを使用しています。

現在のところ、すべてが機能しています。マウスクリックを収集でき、基本的な計算を使用して、クリックされたイベントのグループを判別しようとしています。これは、クリック時のマウスのX位置に基づいており、ScrollViewerウィンドウのスクロールを考慮に入れています。残念ながら、私は正しい「ビン」を取得していません。

私が試した他のこと:

  • 所属するビンに関する情報を含むタグをcanvas要素に追加します。私が取得したコードの背後にあるItemsSourceには、クリックされたイベントだけでなく、すべてのイベントが含まれているため、これは機能しませんでした。これがItemsSourceにアクセスする方法です。

    private void ItemsControl_PreviewMouseLeftButtonDown( object sender, MouseButtonEventArds e)
    {
        var control = (ItemsControl)sender;
        var isource = control.ItemsSource;
        Debug.Assert(isource != null);
    }
    
  • itemscontrolに入れる前に、これをスタックパネルの中に入れ子にしてみました。これは、ネストされたリストがある場合は機能しましたが、リストのリストがなくなると機能しなくなりました。現時点でも、それが実行可能な選択肢になるかどうかはわかりません。

これのために私が持っているXAML(それを見たいと思うかもしれない人のために)は以下の通りです:

対象となるメインのItemsControlは、ScrollViewerのキャンバスにイベントを描画するために使用されるデータテンプレートを格納する最後のものです。他の2つは、タイムラインの下部に沿って使用するタイムスタンプ用です。

<UserControl 
    x:Class="Removed.Views.TransitionTimeline"
    x:ClassModifier="internal"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    mc:Ignorable="d" 
    d:DesignHeight="300" d:DesignWidth="300">
  <UserControl.Resources>
    <Style x:Key="ISTCanvasStyle" TargetType="Canvas">
      <Setter Property="Background" Value="Transparent" />
    </Style>

    <Style x:Key="ISTBorderStyle" TargetType="Border">
      <Setter Property="BorderThickness" Value="3" />
      <Setter Property="BorderBrush" Value="{Binding ColorBrush}" />
      <Setter Property="CornerRadius" Value="8" />
      <Setter Property="HorizontalAlignment" Value="Center" />
      <Setter Property="VerticalAlignment" Value="Center" />
      <Setter Property="Background" Value="#FF555555" />
      <Setter Property="Height" Value="50" />
      <Setter Property="Width" Value="50" />
    </Style>

    <Style x:Key="ISTTextBlockStyle" TargetType="TextBlock">
      <Setter Property="Text" Value="{Binding ShortName}" />
      <Setter Property="HorizontalAlignment" Value="Center" />
      <Setter Property="VerticalAlignment" Value="Center" />
      <Setter Property="FontWeight" Value="Bold" />
      <Setter Property="FontSize" Value="40" />
      <Setter Property="Foreground" Value="White" />
      <Setter Property="TextAlignment" Value="Center" />
    </Style>

    <Style x:Key="EventCountTextStyle" TargetType="TextBlock">
      <Setter Property="Text" Value="{Binding ExtraEvents}" />
      <Setter Property="Canvas.Top" Value="-15" />
      <Setter Property="Canvas.Left" Value="-25" />
      <Setter Property="FontWeight" Value="Bold" />
      <Setter Property="FontSize" Value="13" />
      <Setter Property="Foreground" Value="{Binding EventTextColorBrush}" />
      <Setter Property="TextAlignment" Value="Center" />
    </Style>

    <DataTemplate x:Key="IndividualStateTransitions">
      <Canvas Margin="{Binding Margin}" Style="{ StaticResource ISTCanvasStyle}" >
        <Border Canvas.Left="-12.5" Style="{ StaticResource ISTBorderStyle}" >
          <TextBlock Style="{StaticResource ISTTextBlockStyle}" />
        </Border>
        <TextBlock Style="{StaticResource EventCountTextStyle}" />
      </Canvas>
    </DataTemplate>

    <DataTemplate x:Key="IST">
      <ItemsControl
          ItemsSource="{Binding}" 
          ItemTemplate="{StaticResource IndividualStateTransitions}"
          PreviewMouseLeftButtonDown="ItemsControl_PreviewMouseLeftButtonDown"
          Width="75">
        <ItemsControl.ItemsPanel>
          <ItemsPanelTemplate>
            <Canvas />
          </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
          <Style TargetType="ContentPresenter">
            <Setter Property="Canvas.ZIndex" Value="{Binding ZIndex}" />
          </Style>
        </ItemsControl.ItemContainerStyle>
      </ItemsControl>
    </DataTemplate>

    <DataTemplate x:Key="BottomTimeBar">
      <Canvas>
        <Line X1="{Binding DashX}" X2="{Binding DashX}" Y1="100" Y2="0" Stroke="#FF646464" StrokeThickness="1" StrokeDashArray="5" StrokeDashCap="Round" />
        <TextBlock Canvas.ZIndex="-999"  Width="50" TextAlignment="Center" Text="{Binding TimerText}" Canvas.Left="{Binding BlockLeft}" 
                       Canvas.Top="85" Foreground="White" Background="#FF444444" FontSize="13" />
      </Canvas>
    </DataTemplate>

  </UserControl.Resources>
  <Grid Name="TimelineGrid" Height="192">
    <Grid.RowDefinitions>
      <RowDefinition Height="92" />
      <RowDefinition Height="100" />
    </Grid.RowDefinitions>

    <Canvas Grid.Row="0" Width="1920" Height="100">
      <ScrollViewer            
          Width="1920"
          Height="100"
          Name="_timelineScrollViewer2" 
          CanContentScroll="True" 
          Background="Transparent"
          HorizontalScrollBarVisibility="Hidden" 
          VerticalScrollBarVisibility="Hidden">
        <Canvas Width="9999" Name="_timelineCanvas2">
        </Canvas>
      </ScrollViewer>
    </Canvas>

    <Canvas Grid.Row="1" Name="MainCanvas" Width="1920" Height="100" >
      <Line X1="0" X2="1920" Y1="0" Y2="0" Stroke="#FFD0D0D0" StrokeThickness="3">
        <Line.Effect>
          <DropShadowEffect BlurRadius="3" ShadowDepth="3" />
        </Line.Effect>
      </Line>
      <Line X1="0" X2="1920" Y1="55" Y2="55" Stroke="#FFD0D0D0" StrokeThickness="3">
        <Line.Effect>
          <DropShadowEffect BlurRadius="3" ShadowDepth="3" />
        </Line.Effect>
      </Line>
      <ScrollViewer            
          Width="1920"
          Height="100"
          Name="_timelineScrollViewer" 
          CanContentScroll="True" 
          Background="Transparent"
          HorizontalScrollBarVisibility="Hidden" 
          VerticalScrollBarVisibility="Hidden"
          PreviewMouseLeftButtonDown="TimelineScrollViewerLeftMouseDown" 
          PreviewMouseLeftButtonUp="LeftMouseUp" 
          PreviewMouseMove="TimelineScrollViewerMouseMove"
          PreviewMouseWheel="TimelineScrollViewerPreviewMouseWheel"
          ScrollChanged="OnTimelineScrollChanged" ClipToBounds="False">
        <Canvas Width="9999" Name="_timelineCanvas">
          <Line Canvas.ZIndex="-1000" Name="CurrentTimeLine" X1="1280" X2="1280" Y1="0" Y2="100" Stroke="#FFD0D0D0" StrokeThickness="3">
            <Line.Effect>
              <DropShadowEffect BlurRadius="3" ShadowDepth="3" />
            </Line.Effect>
          </Line>
          <ItemsControl ItemsSource="{Binding BottomTimeBarData}" ItemTemplate="{StaticResource BottomTimeBar}" Canvas.Left="{Binding LeftScroll}">
            <ItemsControl.ItemsPanel>
              <ItemsPanelTemplate>
                <Canvas />
              </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
          </ItemsControl>
          <ItemsControl ItemsSource="{Binding BottomTimeBarDataPast}" ItemTemplate="{StaticResource BottomTimeBar}" Canvas.Left="{Binding LeftScroll}">
            <ItemsControl.ItemsPanel>
              <ItemsPanelTemplate>
                <Canvas />
              </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
          </ItemsControl>

          <ItemsControl ItemsSource="{Binding DisplayObject}" ItemTemplate="{StaticResource IndividualStateTransitions}" Canvas.Left="{Binding LeftScroll}" Canvas.Top="15" Margin="0.0, 25.5"
                        PreviewMouseLeftButtonDown="ItemsControl_PreviewMouseLeftButtonDown" >
            <ItemsControl.ItemsPanel>
              <ItemsPanelTemplate>
                <StackPanel />
              </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemContainerStyle>
              <Style TargetType="ContentPresenter">
                <Setter Property="Canvas.ZIndex" Value="{Binding ZIndex}" />
              </Style>
            </ItemsControl.ItemContainerStyle>
          </ItemsControl>
        </Canvas>
      </ScrollViewer>
    </Canvas>
  </Grid>
</UserControl>

ご不明な点がございましたら、お気軽にお問い合わせください。何が起こっているのかを明確にできてうれしいです。最終的に私が探しているのは、ユーザーが表示したい「ビン」を判別する方法です。

更新1:描画されたオブジェクトに関する追加情報。

独自の内部クラスを定義したので、この場合にバインドされているObservableCollectionのデータ型として使用できます。

internal class DisplayObjects : INotifyPropertyChanged
{
    private int _elementbin;
    public int ElementBin
    {
        get { return _elementbin; }
        set
        {
            _elementbin = value;

            if ( PropertyChanged != null )
                PropertyChanged( this, new PropertyChangedEventArgs( "ElementBin" ) );
        }
    }

    private string _shortName;
    public string ShortName
    {
        get { return _shortName; }
        set
        {
            _shortName = value;

            if ( PropertyChanged != null )
                PropertyChanged( this, new PropertyChangedEventArgs( "ShortName" ) );
        }
    }

    private string _margin;
    public string Margin
    {
        get { return _margin; }
        set
        {
            _margin = value;

            if ( PropertyChanged != null )
                PropertyChanged( this, new PropertyChangedEventArgs( "Margin" ) );
        }
    }

    private string _extraevents;
    public string ExtraEvents
    {
        get { return _extraevents; }
        set
        {
            _extraevents = value;

            if ( PropertyChanged != null )
                PropertyChanged( this, new PropertyChangedEventArgs( "ExtraEvents" ) );
        }
    }

    public Color EventTextColor { get; set; }

    public SolidColorBrush EventTextColorBrush
    {
        get
        {
            return new SolidColorBrush( EventTextColor );
        }
    }

    private int _zindex;
    public int ZIndex
    {
        get { return _zindex; }
        set
        {
            _zindex = value;

            if ( PropertyChanged != null )
                PropertyChanged( this, new PropertyChangedEventArgs( "ZIndex" ) );
        }
    }

    public Color Color { get; set; }

    public SolidColorBrush ColorBrush
    {
        get { return new SolidColorBrush( Color ); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

イベントアグリゲーターを購読しているので、送信されたイベントをキャッチできます。OnEventReceivedこれが私のメソッドをトリガーします。このメソッドについて知っておく必要があるのは、受信したイベントから情報を取得し、イベントが発生した時間に基づいて、どのビンに分類するかを決定することだけです。DrawObjectこれにより、私のメソッドが呼び出されます。

これをエミュレートしたい場合は、stvmの使用をハードコードされたものに置き換えることができます。そのための実際のデータを提供することはできません。stvm.Colorは、イベントタイプを示す単なる色です。stvm.ShortNameは1文字です。_binsはList<Int>、それぞれのビンで発生したイベントの総数を追跡するだけです。 positionそれが属するビンにあるイベントの数です。したがって、最初のイベントの場合は1になり、10番目のイベントの場合は10になります。

ここでのFindLastVisualChildメソッドは、特定のビンの3番目のDisplayObjectの場所のインデックスを返すことを目的としています。これにより、「+Xmore」というタグを更新できます。スタック内のイベント。

private void DrawObject( EventViewModel stvm, int position, int bin )
{
    var margin = "";
    var zindex = 0;
    var left = bin * 75.0;
    var textColor = Colors.Transparent;
    var extraEvents = "+ ";
    switch ( position )
    {
        case 1:
            left += 12.5;
            margin = left + ", -5";
            zindex = 3;
            DisplayObject.Add( new DisplayObjects
            {
                Color = stvm.Color,
                ShortName = stvm.ShortName,
                Margin = margin,
                ZIndex = zindex,
                EventTextColor = textColor,
                ExtraEvents = extraEvents,
                ElementBin = bin
            } );
            break;
        case 2:
            left += 22.5;
            margin = left + ", -15";
            zindex = 2;
            DisplayObject.Add( new DisplayObjects
            {
                Color = stvm.Color,
                ShortName = stvm.ShortName,
                Margin = margin,
                ZIndex = zindex,
                EventTextColor = textColor,
                ExtraEvents = extraEvents,
                ElementBin = bin
            } );
            break;
        case 3:
            left += 32.5;
            margin = left + ", -25";
            zindex = 1;
            DisplayObject.Add( new DisplayObjects 
            { 
                Color = stvm.Color, 
                ShortName = stvm.ShortName, 
                Margin = margin, 
                ZIndex = zindex, 
                EventTextColor = textColor, 
                ExtraEvents = extraEvents,
                ElementBin = bin
            } );
            break;
        default:
            //left += 32.5;
            //margin = left + ", -25";
            //DisplayObject.Add( new DisplayObjects { Color = stvm.Color, ShortName = stvm.ShortName, Margin = margin, ZIndex = zindex, EventTextColor = textColor, ExtraEvents = extraEvents } );
            extraEvents += ( _bins[bin] - 3 ) + " more.";
            var test = FindLastVisualChild( bin );
            DisplayObject[test].EventTextColor = Colors.White;
            DisplayObject[FindLastVisualChild( bin )].ExtraEvents = extraEvents;
            break;
    }
}

private int FindLastVisualChild( int bin )
{
    var sum = 0;
    for ( var idx = 0; idx <= bin; idx++ )
        if ( _bins[idx] <= 3 )
            sum += _bins[idx];
        else
            sum += 3;
    return ( sum - 1 );
}
4

2 に答える 2

1

ここを使ってみe.OriginalSourceましたか?

私は明らかにあなたのプロジェクトを再現できませんでしたが、私が推測できる限り、これを使用してクリックしたキャンバスを取得できるはずです:

var canvas = e.OriginalSource as Canvas

そして、キャンバスの DataContext であることがわかっているので、対応するアイテムを簡単に取得できるはずです。

var item = canvas.DataContext as MyItemViewModel;

しかし、私が書いたように、これは当て推量です。コレクションにアイテムを追加するコードなど、アプリでより明確に表示されるように、より多くの情報を投稿する必要があります。

于 2012-07-19T13:22:01.487 に答える
0

ここでDavidとRachelに感謝します。彼らの意見から、私はついに仕事に取り掛かったという答えにたどり着きました。

この後やって来てそれを読む人のための参考として。これが私がしたことです。

のデータテンプレートで、CanvasIndividualStateTransistionsに追加しTag = "{Binding ElementBin}"ました。次に、イベントをビンにフィルタリングするコードで、その要素が属するビン番号を追加しました。

レイチェルの提案に従って、マウスクリックイベントをこのキャンバスまで移動しました。

背後にあるコードで、Davidがこれを取得するために提案していたものを適応させました。

OnMouseClickEventメソッドで...

var control = (Canvas)sender;
var item = control.DataContext as DisplayObjects;
var itembin = item.ElementBin;

これにより、そのイベントのグループが属するビンが得られ、次のステップに進むことができます。

みんな(そしてギャル)にもう一度感謝します!

于 2012-07-19T14:11:49.840 に答える