2
votes

I am attempting to put all my timing logic into an attached behavior. My goals are:

(Using Visual Studio 2010.)

  1. Simplify my XAML code by replacing many MultiValue triggers,
  2. Change the background color of each ListViewItem based on a timer and data within the ListViewItem,
  3. Use a singleton pattern for the timer as I only want one timer for all my ListViewItems.

My problems are:

  1. How to have the display watch for color changes
  2. Get the specific ListViewItem instance when the timer event fires ("this" is not allowed), and
  3. Removing all memory footprints of the static properties and events when the user control holding the ListView and its items is closed.

Problem #3 may be done automatically by WPF and C# (I'm not sure). #1 poses a problem, because if I understand attached properties correctly, the property will only set the background color on initialization so update to the background color will not be made by the simple XAML shown below. Is a Trigger necessary?

How can this be done?

TIA

Here is what I have so far:

XAML

 <Style x:Key="listViewItemStyle" TargetType="{x:Type ListViewItem}">
            <Setter Property="v:ListViewItemBehavior.MyValue" Value="{Binding}"/>

--IS A TRIGGER NEEDED HERE TO UPDATE THE BACKGROUND COLOR, AND IF SO 
HOW IS IT BOUND TO THE CLASS OF THE ATTACHED PROPERTY WHICH IS NOT IN THE  
VIEW MODEL?
 </Style>

Code

 // Using singleton pattern to create one time to be shared among all ListViewItem instances.
    public static class ListTimer
    {
        // Reasons for using a DispatcherTimer opposed to a System.Timers.Timer are that the DispatcherTimer runs on the same thread as the 
        // Dispatcher and a DispatcherPriority can be set on the DispatcherTimer. Timer runs in its own thread.
        private static readonly Timer listTimer;

        static ListTimer()
        {
            listTimer = new Timer { AutoReset = true, Enabled = true, Interval = 10 * 1000 };  // Interval in milliseconds
            listTimer.Elapsed += listTimer_Elapsed;
        }

        static void listTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (ListTimerEvent != null)
            {
                ListTimerEvent(sender, e);       
            }  
        }

        public static event EventHandler ListTimerEvent;
    }

    // Static classes can not implement an interface. (Hence : INotifyPropertyChanged can not be used).
    public static class ListViewItemBehavior 
    {

        public static string GetMyValue(DependencyObject obj)
        {
            return (string)obj.GetValue(MyValueProperty);
        }

        public static void SetMyValue(DependencyObject obj, string value)
        {
            obj.SetValue(MyValueProperty, value);
        }

        // Using a DependencyProperty as the backing store for MyValue.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MyValueProperty =
            DependencyProperty.RegisterAttached("MyValue", typeof(Object), typeof(ListViewItemBehavior), new UIPropertyMetadata(null, OnMyValueChanged));

        static void OnMyValueChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
        {
            var item = depObj as ListViewItem;
            if (item == null) return;

  --EACH INSTANCE HAS ITS OWN VALUE ON VIEW_ENCOUNTERTIME--
            View_encountertime vt = item.DataContext as View_encountertime;

            ListTimer.ListTimerEvent +=new EventHandler(ListTimer_ListTimerEvent);

        }

        static void ListTimer_ListTimerEvent(object sender, EventArgs e)
        {
            Timer timer = sender as Timer;


            var y = this.GetValue(MyValueProperty);  <-- WRONG! CAN'T USE "THIS"
           -- I would put logic to set the background color here for 
           -- each instance ...if I knew how!

        }
2

2 Answers

1
votes

You can use an anonymous method in the OnMyValueChanged to specify the timer event handler. This way, you will have access to the specific ListViewItem.

static void OnMyValueChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
    var item = depObj as ListViewItem;
    if (item == null) return;

    View_encountertime vt = item.DataContext as View_encountertime;

    ListTimer.ListTimerEvent += (sender, e) =>
    {
        Timer timer = sender as Timer;


        var y = item.GetValue(MyValueProperty);
        <--  put logic to set the background color here

    }
}
1
votes

My final solution that seems to work without any explicit triggers with the timer as above is:

static void OnMyValueChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs args)
    {
        var item = depObj as ListViewItem;
        if (item == null) return;

       // This is unable to pass the instance data:  ListTimer.ListTimerEvent +=new EventHandler(ListTimer_ListTimerEvent);
       // Use an anonymous method in the OnMyValueChanged to specify the timer event handler. This will give access to the specific 
       // ListViewItem.
        ListTimer.ListTimerEvent += (sender, e) =>
        {
            Timer timer = sender as Timer;

            // The Timer is running on a different thread then the ListViewItem item
            item.Dispatcher.Invoke((Action)(() =>
            {
                View_encountertime vt = item.DataContext as View_encountertime;
                if (vt == null) return;


                if (vt.Tcheckout != null || vt.Notseen == true)
                {
                    item.Background = Brushes.White;
                    return;
                }

                DateTime z = (DateTime)vt.Tencounter;

                // get current time.
                DateTime now = DateTime.Now;
                TimeSpan ts = now.Subtract(z);

                item.Background = Brushes.Red;
                if (ts.CompareTo(WaitingTime.ninetymin) < 1) item.Background = Brushes.Orange;          
                if (ts.CompareTo(WaitingTime.seventyfivemin) < 1) item.Background = Brushes.Yellow;     
                if (ts.CompareTo(WaitingTime.sixtymin) < 1) item.Background = Brushes.Green;            
                if (ts.CompareTo(WaitingTime.fortyfivemin) < 1) item.Background = Brushes.Turquoise;    
                if (ts.CompareTo(WaitingTime.thirtymin) < 1) item.Background = Brushes.Blue;            
                if (ts.CompareTo(WaitingTime.fifteenmin) < 1) item.Background = Brushes.Violet;         

            }));


        };


    }

As it turns out, Background is a dependency property of the ListViewItem, so no explicit trigger is needed to update the display when the Background property is changed. The UI will take care of itself.