Home > Software design >  How WPF Moving GUI Binding
How WPF Moving GUI Binding

Time:10-19

I am writing this because I had difficulties in implementing a specific function during WPF implementation.

For data model latitude and longitude (location in current window)

MainViewModel is I am managing it as an observableCollection.

In xaml, the upper and longitude values ​​for each model list were displayed,

The part that implements the button control in the form of an image to move according to the location information that is continuously updated in xaml is blocked, so I am posting this.

In addition, I want to display a line for the azimuth or each component, but I also want to implement this so that the line moves according to the changing value.

Is there a method that is usually used for these changing values ​​or is there a method that is mainly used in practice? In the case of Winform, I used the method of drawing a line using a Graphics object, but if anyone knows how to display it in real time by moving it in real time in C# WPF and binding the position value, I would appreciate it if you could share it.

CodePudding user response:

For the positioning of controls or display-elements, you can use binding to a property in your ViewModel.
In the example below I am using a Canvas and bind the Canvas.Top attribute of a line to the LinePos property in the Viewmodel.
An alternative could be to bind the Rendertransform instead...

<Canvas>
    <Line  X1="0" Y1="10"  X2="500" Y2="10" Stroke="Blue" StrokeThickness="3" Canvas.Top="{Binding LinePos}" />        
</Canvas>

And the ViewModel:

public class MainViewModel : ObservableObject
{
    private int _linePos;
    private DispatcherTimer _timer = new();

    // binding properties

    public int LinePos
    {
        get => _linePos;
        set => SetProperty(ref _linePos, value);
    }


    public MainViewModel()
    {
        _timer.Interval = TimeSpan.FromMilliseconds(10);
        _timer.Tick  = OnTimerTick;
        _timer.Start();
    }

    private void OnTimerTick(object? sender, EventArgs e)
    {
        LinePos = (LinePos 1) % 500;
    }
}

CodePudding user response:

In my previous answer I demonstrated a way to position a line based on a property in the viewmodel. However, this only answers part of your question.
In the example below I will demonstrate how to have an ObservableCollection with positions that will be reflected on screen by showing the positions as text at the screen location corresponding with their values.

To start with, while the ObservableCollection can be used as an Itemsource, we must make sure the Items themselves are suitable to be used as viewmodels: So, you could declare a Positionclass as follows (using 'CommunityToolkit.Mvvm'):

using CommunityToolkit.Mvvm.ComponentModel;

namespace ViewModels;
public partial class Position : ObservableObject
{
    [ObservableProperty]    
    private double _x;

    [ObservableProperty]
    private double _y;

    public Position(double x = 0.0, double y = 0.0)
    {
        X = x;
        Y = y;
    }
}

Now you can declare a MainViewModel containing a ObservableCollection<Position> property that will be used as an itemsource. For demonstration purposes, the values for the positions will be changed continuously based upon a DispatchTimer:

using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.ObjectModel;
using System.Windows.Threading;

namespace PresentationLayer.ViewModels;
public class MainViewModel : ObservableObject
{

    private DispatcherTimer _timer = new();
    private Random _random = new();

    // binding properties
    public ObservableCollection<Position> TextPositions { get; private set; } = new();

    public MainViewModel()
    {
        TextPositions.Add(new(10, 10));
        TextPositions.Add(new(20, 100));
        TextPositions.Add(new(50, 300));
        _timer.Interval = TimeSpan.FromMilliseconds(100);
        _timer.Tick  = OntimerTick;
        _timer.Start();
    }

    private void OntimerTick(object? sender, EventArgs e)
    {
        foreach (var item in TextPositions)
        {
            item.X = (item.X   _random.Next(30)) % 600;
            item.Y = (item.Y   _random.Next(20)) % 350;
        }
    }
}

Finally, this MainViewModel can be used as datacontext for a WPF-Window displaying the positions in an ItemsControl using a datatemplate to construct the Textvalues to be displayed.
The positioning is handled using an ItemsControl.ItemContainerStyle attribute binding the position of the items.
Note: for this to work, you need to specify the ItemsPanel should be of type Canvas since the binding for the Location uses Canvas.Top and Canvas.Left.
The MainWindow (using a MainViewModelinstance as datacontext):

<Canvas>
        <ItemsControl ItemsSource="{Binding TextPositions}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemContainerStyle>
                <Style>
                    <Setter Property="Canvas.Top" Value="{Binding Y}"/>
                    <Setter Property="Canvas.Left" Value="{Binding X}"/>
                </Style>
            </ItemsControl.ItemContainerStyle>
            <ItemsControl.ItemTemplate >
                <DataTemplate>
                    <Border BorderBrush="Blue" BorderThickness="1" CornerRadius="2" Background="LightSkyBlue">
                    <WrapPanel>
                        <TextBlock Text="{Binding X}" />
                        <TextBlock Text=", "/>
                        <TextBlock Text="{Binding Y}" />
                    </WrapPanel>
                    </Border>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Canvas>
  • Related