0

I want to create a container that fills the inside with a color according to a parameter that increases.

for example i created the following example: MainWindow:

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Border BorderBrush="Black" BorderThickness="1" Width="100" Height="200">
        <Rectangle VerticalAlignment="Bottom" Height="{Binding Height}"  Width="100" Fill="Red" MaxHeight="200"/>
    </Border>
</Grid>

Engine.cs:

class Engine
{
    public ViewModel viewModel = new ViewModel();

    public void process()
    {
        Thread a = new Thread(() =>
        {
            while (viewModel.Height < 200)
            {
                ChangeHeight();
                Thread.Sleep(1000);
            }
        });
        a.IsBackground = true;
        a.Start();

    }
    public void ChangeHeight()
    {
        viewModel.Height++;            
    }
}

ViewModel is the datacontext. It works great but I think there's something much better than what i did. Moreover, I need the transfer bewtween ChangeHeight() to be smooth meaning an animation is required here.

Is there any good example or guidance?

UPDATE I'm adding the view model code:

namespace WpfApplication1

{ public class ViewModel : INotifyPropertyChanged { private int m_height = 0; public int Height { get { return m_height; } set { m_height = value; NotifyPropertyChanged("Height"); } }

    #region "PropertyChanged Event"
    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    #endregion
}

}

Joe
  • 171
  • 2
  • 19
  • Did you implement `INotifyPropertyChanged` in the ViewModel? Can you add its code to the question? – CKII Feb 28 '17 at 14:34
  • Why do you feel it is not smooth ? What is happening here. Can you add more details. – Versatile Feb 28 '17 at 14:37
  • Refer: http://stackoverflow.com/questions/3762576/wpf-backgroundworker-vs-dispatcher – Versatile Feb 28 '17 at 14:42
  • thank you for the reply, CKII- i've added an update to my question Versatile - if i increase the height by 15 and not by 1 it will jump to the height i mention and i need it to smoothly grow. – Joe Feb 28 '17 at 14:43

2 Answers2

1

Instead of programmatically animating a view model property, you may have an attached property in the view that animates a target property like e.g. Height:

public static class Animated
{
    private static Duration duration = TimeSpan.FromSeconds(5);

    public static readonly DependencyProperty HeightProperty =
        DependencyProperty.RegisterAttached(
            "Height", typeof(double), typeof(Animated),
            new PropertyMetadata(HeightPropertyChanged));

    public static double GetHeight(DependencyObject obj)
    {
        return (double)obj.GetValue(HeightProperty);
    }

    public static void SetHeight(DependencyObject obj, double value)
    {
        obj.SetValue(HeightProperty, value);
    }

    private static void HeightPropertyChanged(
        DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        var element = obj as FrameworkElement;

        if (element != null)
        {
            var to = (double)e.NewValue;
            var animation = double.IsNaN(element.Height)
                ? new DoubleAnimation(0, to, duration)
                : new DoubleAnimation(to, duration);

            element.BeginAnimation(FrameworkElement.HeightProperty, animation);
        }
    }
}

You would use it in XAML like this:

<Rectangle Fill="Red" Width="100" Height="0"
    local:Animated.Height="{Binding TargetHeight}"/>

and just set the TargetHeight view model property to the desired target value.

Clemens
  • 123,504
  • 12
  • 155
  • 268
  • it works great i just want to understand something, i tried to change the animated that it will have Width Propety and not Height. how can it be done? – Joe Feb 28 '17 at 15:10
  • Pass `FrameworkElement.WidthProperty` to BeginAnimation (in the PropertyChangedCallback of another attached property). Note that the animation duration may also be declared as attached property so that it can be set in XAML. – Clemens Feb 28 '17 at 15:12
  • But in the xaml can i add local:Animated.Width?i tried that but i can't see this property – Joe Feb 28 '17 at 15:20
  • Add another attached property called `Width` by copying all the code above and replacing every occurance of "Height" by "Width". See the [Attached Properties Overview](https://msdn.microsoft.com/en-us/library/ms749011(v=vs.110).aspx) article on MSDN for details. – Clemens Feb 28 '17 at 15:22
0

Use pure animation

 <Border BorderBrush="Black" BorderThickness="1" Width="100" >
        <Rectangle VerticalAlignment="Bottom" Height="50" Width="100" Fill="Red" >
            <Rectangle.Triggers>
                <EventTrigger RoutedEvent="Loaded">
                    <BeginStoryboard>
                        <Storyboard Storyboard.TargetProperty="Height">
                            <DoubleAnimation From="0" To="{Binding Height}" Duration="0:0:20"/>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
            </Rectangle.Triggers>
        </Rectangle>
    </Border>

Refactoring your current approach, use Task and async/await which is modern way to write multi-threaded programs.

    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        Task.Factory.StartNew(() => { App.Current.Dispatcher.Invoke(async () => 
            {
                while (this.Height < 200)
                {
                    await Task.Delay(1000);
                    ++viewModel.Height;
                }
            }); 
        });
    }
AnjumSKhan
  • 9,647
  • 1
  • 26
  • 38