0
votes

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
}

}

2
Did you implement INotifyPropertyChanged in the ViewModel? Can you add its code to the question?CKII
Why do you feel it is not smooth ? What is happening here. Can you add more details.Versatile
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

2 Answers

1
votes

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.

0
votes

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