I am new to WPF and I am having issues trying to update the main window UI after a change.
I have the following MainWindow.xaml (I will show a part of it because it is large)
<Window
x:Class="SimpleDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:HelixToolkit="clr-namespace:HelixToolkit.Wpf;assembly=HelixToolkit.Wpf"
xmlns:local="clr-namespace:SimpleDemo"
Title="Plot 3D data"
Width="680"
Height="680" WindowStartupLocation="CenterScreen">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
Next important part is the 3D plot section of the MainWindow, where I Bind the Content of the plot with my Model3D object named as Model.
<!-- Remember to add light to the scene -->
<HelixToolkit:SunLight />
<!-- The content of this visual is defined in MainViewModel.cs -->
<ModelVisual3D Content="{Binding Path = Model, Mode=OneWayToSource,NotifyOnTargetUpdated=True, UpdateSourceTrigger=PropertyChanged}" />
<!-- You can also add elements here in the xaml -->
<HelixToolkit:GridLinesVisual3D
Width="800"
Length="800"
MajorDistance="15"
MinorDistance="15"
Thickness="0.05" />
</HelixToolkit:HelixViewport3D>
<StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal">
<Button Margin="5" Click="Backward_Click">Backward</Button>
<Button Margin="5" Click="Forward_Click">Forward</Button>
</StackPanel>
The idea of the program is to read the first 500 data points (x,y,z) from a txt file and then creating a AddRectangularMesh() from those using Helix Toolkit. I do this for multiple set of points and I finally have a mesh with multiple AddRectangularMesh() objects. After that, I display the final result to the HelixViewport3D. In the UI I have two buttons Forward and Backward. The forward button is trying to draw the next 500 points from the txt file.
In my MainWindow.xaml.cs file I call the Event handlers of the buttons which calls the Functions in MainViewModel.cs file where all the functions for that data points are calculated and the final result will be saved on Model3D Model object, and then must be drawn on the UI.
Button calls the Forward_click event and Increases the counter to read the next data points
<Button Margin="5" Click="Forward_Click">Forward</Button>
After I press the button the Event Handlers is called but it is null, so the UI is never update and I can't show the next data points. In my code I have implemented the Event handler, to listen on the changes of the Model, so after the execution of the functions the model object will be updated and then call the handler to update the UI.
The MainViewModel Class is defined as:
public class MainViewModel : Window, INotifyPropertyChanged
and the event handlers as below:
private Model3D model;
public Model3D Model
{
get { return model; }
set
{
model = value;
OnPropertyChanged(nameof(Model));
}
}
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, e);
}
protected void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
I put breakpoints on the code, and when I press the Button, the handler is called but is null and then nothing is updated or done, the function calculates the new Model3D object with new points, but does not draw the new result.
If I run the same function on public MainViewModel()
section the first 500 points are shown, but it due to the first run of the window.
This is how the Model object is updated after each button press.
public void CoreCalculations()
{
for (int i = 0 ; i < 500; i )
{
//read File Line
// --- Do something with the points ---
// create a mesh using the points and AddRectangularMesh
}
MeshGeometry3D mesh = meshBuilder.ToMesh(true);
Material blueMaterial = MaterialHelper.CreateMaterial(Colors.Blue);
modelGroup.Children.Add(new GeometryModel3D { Geometry = mesh, Transform = new TranslateTransform3D(2, 0, 0), Material = blueMaterial, BackMaterial = blueMaterial });
Model = modelGroup;
}
CodePudding user response:
Try changing OneWayToSource
to OneWay
. From the documentation
Updates the source property when the target property changes.
And you probably want to do it the other way around. Update the target whenever the model changes.
It is a bit odd that MainViewModel
inherits from Window
. I would suggest taking a look at the helix3D "SimpleDemo". Just to confirm that changing the model works, Make the mainViewModel inherit INotifyPropertyChanged, and add the following code:
private async Task RemoveModel()
{
await Task.Delay(5000);
Model = null;
OnPropertyChanged(nameof(Model));
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
And call RemoveModel
in the constructor. That should make the model be removed after 5s. Note that the example code is suitable for debugging, and nothing else.
You should also be able to use the live visual tree explorer to help debug the UI.