Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 8259125
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 8, 20262026-06-08T02:47:12+00:00 2026-06-08T02:47:12+00:00

I’ve hit a little road block with a timeline I am working on developing

  • 0

I’ve hit a little road block with a timeline I am working on developing in a MVVM based WPF application.

My desire is to be able to tell what “bin” the user wishes to view as there are generally more events than what are displayed in the View. This would essentially be a small little stackpanel that is developed in the code-behind and displayed above the bin the user selected. More details are below.

The basic background is this:

  • I am logging events as they occur in the ViewModel, I’ll refer to them as events.

  • I am displaying them as a 50×50 canvas (one per event) in the View on the timeline. The nuance here is that I have a very limited amount of space so I limit the actual number of events shown to a stack of 3 that are slightly offset (just imagine cards being stacked behind eachother with the top and right of the card beneath being shown) of eachother for each of the timeblocks.

  • At each tick of the time (every 30 seconds) the canvas is scrolled to the left 75 pixels and all of the drawn elements move with it as well, of course. This also sets up the “bins” for the events. Essentially everything between 0 and 29.9 seconds is in bin 0, 30 – 59.9 is bin 1 and so on.

  • I am using a PreviewMouseLeftButtonDown event linked to the ItemsControl that is responsible for displaying the bound events.

As it stands right now everything works. I can collect the mouse click and I’ve been trying to use some basic math to determine what group of events was clicked. This has been based on the X position of the mouse at the time of the click and taking into account any scrolling of the ScrollViewer window. Unfortunately I haven’t been getting a correct “bin”.

Other things I have tried:

  • Add in a tag to the canvas element containing the information about what bin it belongs to. This didn’t work because the ItemsSource in the code behind that I get contains every event, not just those that were clicked on. This is how I access the ItemsSource.

    private void ItemsControl_PreviewMouseLeftButtonDown( object sender, MouseButtonEventArds e)
    {
        var control = (ItemsControl)sender;
        var isource = control.ItemsSource;
        Debug.Assert(isource != null);
    }
    
  • I’ve tried nesting this inside of a stackpanel before putting it into the itemscontrol. This worked when I had a nested list but it broke down after I got away from having a list of lists. I’m not sure if it would be a viable option at this point either.

The XAML that I have for this (for those who might want to see it) is below:

The main ItemsControl of interest is the last one as it houses the datatemplate that is used to draw the events on the canvas in the ScrollViewer. The other two are just for the timestamps that I use along the bottom of the timeline.

<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>

If you have any questions please let me know. I’m more than happy to clarify what is going on. Ultimately what I’m looking for is a way to determine what “bin” the user wishes to view.

Update 1: Additional information about the drawn objects.

I have defined my own internal class so I can use it as the datatype for my ObservableCollection that is what is being bound to in this case.

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

I am subscribed to an event aggregator so I can catch the events as they are sent. This triggers my OnEventReceived method. All you need to know about this method is that it takes information from the event it receives and determines what bin it should fall into which is based on the time the event occured. This triggers a call to my DrawObject method.

If you wish to try and emulate this you can just replace the uses of stvm with some hardcoded stuff. I can’t provide the actual data for that. stvm.Color is just a color that denotes an event type. stvm.ShortName is just a single letter. _bins is just a List<Int> that keeps track of the total number of events that have occured in the respective bin. position is just the number event it is in the bin it belongs to. So if it’s the first event that’ll be a 1. If it’s the tenth event it’ll be a 10.

The FindLastVisualChild method here is just meant to return the index of the location for the 3rd DisplayObject for a given bin. This allows me to update the tag that says there a “+ X more.” events in the stack.

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 );
}
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-08T02:47:13+00:00Added an answer on June 8, 2026 at 2:47 am

    Big thanks to David and Rachel here. Their input led me to the answer that I finally got working.

    Just as a reference for anyone who comes along after this and reads it. Here is what I did.

    In the datatemplate for the IndividualStateTransistions I added in Tag = "{Binding ElementBin}" on the Canvas there. I then added the bin number that, that element belongs to in the code that filters the events into bins.

    I moved the mouse click event up to this Canvas as well per Rachel’s suggestion.

    In the code behind I adapted what David was suggesting to get this:

    In the OnMouseClickEvent method…

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

    This gives me the bin that, that group of events belongs to and allows me to continue onto the next step.

    Thanks again guys (and gals)!

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

link Im having trouble converting the html entites into html characters, (&# 8217;) i
I have a string like this: La Torre Eiffel paragonata all&#8217;Everest What PHP function
I'm parsing an RSS feed that has an &#8217; in it. SimpleXML turns this
Configuring TinyMCE to allow for tags, based on a customer requirement. My config is
I'm working with an upstream system that sometimes sends me text destined for HTML/XML
I have a .ini file as follows: [playlist] numberofentries=2 File1=http://87.230.82.17:80 Title1=(#1 - 365/1400) Example
That's pretty much it. I'm using Nokogiri to scrape a web page what has
I have just tried to save a simple *.rtf file with some websites and
I want to count how many characters a certain string has in PHP, but
I would like to count the length of a string with PHP. The string

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.