content dialog with points

Eduardo Gomez Romero 1,315 Reputation points
2025-03-08T19:54:20.94+00:00

Hello

I have a problem with my app and the content dialog

So I have an app, that creates an STL based on a drawing

I also wanted to open a content dialog, with the canvas on one side and the point on other side. The idea is to draw a point, when I heck a checkbox and remove it when I uncheck (This will tell the user in Realtime what is happening)

MainviewModel

using NanoFlow.Views.Dialog;

using System.Globalization;
using System.IO;
using System.Linq;

using Path = System.IO.Path;

namespace NanoFlow.ViewModels {

    public partial class MainViewModel : ObservableObject {

        private const string NanoFlowFolder = "NanoFlow";

        private const int gridSpacing = 20; // Spacing between grid lines in pixels

        private readonly int _canvasSize = 378; // Size of the canvas in pixels

        private bool _areGridMarginsAdded = false;

        private readonly List<Line> _gridMarginLines = []; // To track grid lines

        private readonly List<TextBlock> _gridTextBlocks = [];

        private Grid? _rootContainer;
        private Canvas? _canvas;
        private PointModel? _previousPoint;

        public readonly ObservableCollection<PointModel> Points = [];

        public readonly ObservableCollection<LineModel> Lines = [];

        public readonly List<Tuple<PointModel, PointModel>> LineData = [];

        public readonly List<PointModel> _selectedPoints = [];

        public void SetRootContainer(object sender, RoutedEventArgs args) {
            _rootContainer = sender as Grid;
        }

        [RelayCommand]
        async Task SaveToSTLAsync() {
            // Create the dialog
            var fileNameDialog = new ContentDialog {
                Title = "Save Design",
                Content = new StackPanel {
                    Children =
                    {
                        new TextBlock { Text = "Enter a name for your design:" },
                        new TextBox { PlaceholderText = "MyDesign.stl", Name = "FileNameTextBox" }
                    }
                },
                PrimaryButtonText = "Save",
                CloseButtonText = "Cancel",
                XamlRoot = _rootContainer?.XamlRoot
            };

            // Show the dialog
            var result = await fileNameDialog.ShowAsync();

            if(result == ContentDialogResult.Primary) {
                // Retrieve the TextBox value
                var stackPanel = (StackPanel)fileNameDialog.Content;
                var textBox = stackPanel.Children.OfType<TextBox>().First();
                var fileName = textBox.Text;

                // Validate the file name
                if(string.IsNullOrWhiteSpace(fileName)) {
                    // Use a default file name if none is provided
                    fileName = "MyDesign.stl";
                }
                else if(!fileName.EndsWith(".stl", StringComparison.OrdinalIgnoreCase)) {
                    // Append the ".stl" extension if it's missing
                    fileName += ".stl";
                }

                // Generate the STL file
                GetLineData(); // Ensure LineData is updated
                ExportSTL(fileName, 5.0);
                Console.WriteLine($"STL file '{fileName}' generated successfully.");
            }
        }

        [RelayCommand]
        void CreateNewDesign() {

            DrawCanvas();

            if(_canvas != null) {
                _canvas!.PointerPressed += Canvas_PointerPressed;
                _canvas.PointerMoved += Canvas_PointerMoved;
                _canvas.PointerReleased += Canvas_PointerReleased;
            }

            Points.Clear();
            Lines.Clear();
            LineData.Clear();

            Console.WriteLine("Create New Design: Canvas initialized and data cleared.");
        }

        [RelayCommand]
        async Task GuidedProcessAsync() {

            Points.Clear();
            _selectedPoints.Clear();
            DrawCanvas();

            for(int i = 0; i < _canvasSize; i += gridSpacing) {
                for(int j = 0; j < _canvasSize; j += gridSpacing) {
                    var point = new PointModel(i, j, DrawPoint);
                    Points.Add(point);
                }
            }

            // Open the dialog and pass the ViewModel
            var dialog = new GuidedProcessDialog(this) {

                XamlRoot = _rootContainer?.XamlRoot
            };
            await dialog.ShowAsync();
        }

        [RelayCommand]
        void AddMargin() {
            if(_canvas == null) {
                return;
            }

            if(_areGridMarginsAdded) {
                RemoveGridMarginsAndTextBlocks();
                _areGridMarginsAdded = false;
            }
            else {
                DrawGridMarginsAndTextBlocks();
                _areGridMarginsAdded = true;
            }

        }

        private void RemoveGridMarginsAndTextBlocks() {
            var allItems = _gridMarginLines.Cast<UIElement>()
                 .Concat(_gridTextBlocks.Cast<UIElement>())
                 .ToList();

            foreach(var element in allItems) {
                _canvas!.Children.Remove(element);
            }

            // Clear both lists afterward
            _gridMarginLines.Clear();
            _gridTextBlocks.Clear();
        }

        private void DrawCanvas() {
            _canvas = new Canvas {
                Background = new SolidColorBrush(Color.FromArgb(255, 0, 128, 0)),
                Width = _canvasSize,
                Height = _canvasSize,
            };

            if(_rootContainer != null) {
                _rootContainer.Children.Add(_canvas);
                Grid.SetRow(_canvas, 1);
            }
        }

        private void DrawGridMarginsAndTextBlocks() {
            if(_canvas == null) {
                return;
            }

            for(int i = 0; i < _canvas!.Width; i += gridSpacing) {
                for(int j = 0; j < _canvas.Height; j += gridSpacing) {
                    // Draw vertical and horizontal lines crossing at intersections
                    if(i == 0) {
                        var horizontalLine = new Line {
                            X1 = 0,
                            Y1 = j,
                            X2 = _canvas.Width,
                            Y2 = j,
                            Stroke = new SolidColorBrush(Colors.Gray),
                            StrokeThickness = 1
                        };
                        _canvas.Children.Add(horizontalLine);
                        _gridMarginLines.Add(horizontalLine);
                    }

                    if(j == 0) {
                        var verticalLine = new Line {
                            X1 = i,
                            Y1 = 0,
                            X2 = i,
                            Y2 = _canvas.Height,
                            Stroke = new SolidColorBrush(Colors.Gray),
                            StrokeThickness = 1
                        };
                        _canvas.Children.Add(verticalLine);
                        _gridMarginLines.Add(verticalLine);
                    }

                    // Add a TextBlock at each intersection
                    var textBlock = new TextBlock {
                        Text = $"({i / gridSpacing},{j / gridSpacing})",
                        Foreground = new SolidColorBrush(Colors.Black),
                        FontSize = 6
                    };
                    Canvas.SetLeft(textBlock, i + 2); // Position slightly offset for visibility
                    Canvas.SetTop(textBlock, j + 2);

                    _canvas.Children.Add(textBlock);
                    _gridTextBlocks.Add(textBlock);
                }
            }
        }

        private void DrawLine(PointModel startPoint, PointModel endPoint) {
            var line = new Line {
                X1 = startPoint.X,
                Y1 = startPoint.Y,
                X2 = endPoint.X,
                Y2 = endPoint.Y,
                Stroke = new SolidColorBrush(Colors.White),
                StrokeThickness = 2,
            };
            _canvas!.Children.Add(line);

            Lines.Add(new LineModel(startPoint, endPoint));
        }

        private void DrawPoint(PointModel point) {

            if(_selectedPoints.Contains(point)) {

                var toRemove = _canvas!.Children.OfType<Ellipse>().FirstOrDefault(e => e.Tag == point);
                if(toRemove != null) {
                    _canvas.Children.Remove(toRemove);
                }

                // Remove the point from the selected points list
                _selectedPoints.Remove(point);
            }
            else {

                var ellipse = new Ellipse {
                    Width = 4,
                    Height = 4,
                    Fill = new SolidColorBrush(Colors.Red)
                };
                Canvas.SetLeft(ellipse, point.X - 2);
                Canvas.SetTop(ellipse, point.Y - 2);
                _canvas!.Children.Add(ellipse);

                // Connect with the previous point if one exists
                if(_selectedPoints.Count > 0) {
                    var lastPoint = _selectedPoints[^1];
                    DrawLine(lastPoint, point);
                }

                _selectedPoints.Add(point);
            }
        }

        public List<Tuple<PointModel, PointModel>> GetLineData() {

            LineData.Clear();
            foreach(var line in Lines) {
                LineData.Add(new Tuple<PointModel, PointModel>(line.Start, line.End));
            }

            return LineData;
        }

        private PointModel3D Create3DPoint(PointModel point, double z) {
            return new PointModel3D(point.X, point.Y, z);
        }

        public List<Tuple<PointModel3D, PointModel3D, PointModel3D, PointModel3D>> Generate3DLines(double thickness) {
            List<Tuple<PointModel3D, PointModel3D, PointModel3D, PointModel3D>> extrudedLines = [];

            foreach(var line in LineData) {
                var startBase = Create3DPoint(line.Item1, 0);
                var endBase = Create3DPoint(line.Item2, 0);
                var startTop = Create3DPoint(line.Item1, thickness);
                var endTop = Create3DPoint(line.Item2, thickness);

                extrudedLines.Add(new Tuple<PointModel3D, PointModel3D, PointModel3D, PointModel3D>(
                    startBase, endBase, endTop, startTop
                ));
            }

            return extrudedLines;
        }

        public void ExportSTL(string filePath, double thickness) {

            GetLineData();
            var extrudedLines = Generate3DLines(thickness);

            // Validate extrudedLines before proceeding
            if(extrudedLines.Count == 0) {
                throw new InvalidOperationException("No 3D geometry created. Check the logic in Generate3DLines.");
            }

            var desinsFolder = Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), NanoFlowFolder);

            if(!Directory.Exists(desinsFolder)) {
                Directory.CreateDirectory(desinsFolder);
            }

            var stlFilePath = Path.Combine(desinsFolder, filePath);

            var stlContent = new StringWriter(CultureInfo.InvariantCulture);
            stlContent.WriteLine("solid Model");

            foreach(var rectangle in extrudedLines) {

                var p1 = rectangle.Item1;
                var p2 = rectangle.Item2;
                var p3 = rectangle.Item3;
                var p4 = rectangle.Item4;

                AddRectangleToSTL(stlContent, p1, p2, p3, p4);

            }

            stlContent.WriteLine("endsolid Model");

            File.WriteAllText(stlFilePath, stlContent.ToString());
        }

        private void AddRectangleToSTL(StringWriter stl, PointModel3D p1, PointModel3D p2,
            PointModel3D p3, PointModel3D p4) {

            // Define two triangles for the rectangle
            AddTriangleToStl(stl, p1, p2, p3); // Triangle 1
            AddTriangleToStl(stl, p1, p3, p4); // Triangle 2
        }

        private void AddTriangleToStl(StringWriter stl, PointModel3D v1, PointModel3D v2, PointModel3D v3) {
            stl.WriteLine("  facet normal 0 0 0"); // Normal vector (set to 0 for simplicity)
            stl.WriteLine("    outer loop");
            stl.WriteLine($"      vertex {v1.X} {v1.Y} {v1.Z}");
            stl.WriteLine($"      vertex {v2.X} {v2.Y} {v2.Z}");
            stl.WriteLine($"      vertex {v3.X} {v3.Y} {v3.Z}");
            stl.WriteLine("    endloop");
            stl.WriteLine("  endfacet");
        }


        #region UI Event Handlers

        private void Canvas_PointerPressed(object sender, PointerRoutedEventArgs e) {
            var position = e.GetCurrentPoint(_canvas).Position;

            // Create the PointModel with the action(s) required
            var currentPoint = new PointModel((int)position.X, (int)position.Y, DrawPoint);

            // Set the previous point for line drawing
            _previousPoint = currentPoint;

            // Trigger the action to draw the point
            DrawPoint(currentPoint);
        }

        private void Canvas_PointerMoved(object sender, PointerRoutedEventArgs e) {
            if(_previousPoint != null && e.Pointer.IsInContact) {
                var position = e.GetCurrentPoint(_canvas).Position;

                // Create the PointModel dynamically with the actions
                var currentPoint = new PointModel((int)position.X, (int)position.Y, DrawPoint);

                // Draw a line between the points
                DrawLine(_previousPoint, currentPoint);

                // Update the previous point
                _previousPoint = currentPoint;
            }
        }

        private void Canvas_PointerReleased(object sender, PointerRoutedEventArgs e) {
            // Reset the previous point to stop drawing
            _previousPoint = null;
        }


        #endregion
    }
}


Model

public partial class PointModel(int x, int y, Action<PointModel> selectAction) : ObservableObject {

    public int X { get; set; } = x;
    public int Y { get; set; } = y;

    private const int GridSpace = 20;

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(DisplayText))]
    private bool isSelected;

    private readonly Action<PointModel> selectAction = selectAction;

    public string DisplayText => $"Point ({X / GridSpace}, {Y / GridSpace})";

    partial void OnIsSelectedChanged(bool value) {

        selectAction?.Invoke(this);

    }
}

Dialog

    Title="Guided Process"
    Width="800"
    Height="600"
    PrimaryButtonText="Close">

    <Grid>
        <!--  Define Columns  -->
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="2*" />
            <!--  Left Side: GridView  -->
            <ColumnDefinition Width="3*" />
            <!--  Right Side: Canvas  -->
        </Grid.ColumnDefinitions>

        <!--  GridView for Point Selection  -->
        <GridView
            Grid.Column="0"
            ItemsSource="{x:Bind ViewModel.Points}"
            SelectionMode="None">
            <GridView.ItemTemplate>
                <DataTemplate x:DataType="local:PointModel">
                    <CheckBox
                        Content="{x:Bind DisplayText}"
                        IsChecked="{x:Bind IsSelected, Mode=TwoWay}" />
                </DataTemplate>
            </GridView.ItemTemplate>
        </GridView>

        <!--  Canvas for Real-Time Drawing  -->
        <Canvas
            x:Name="DrawingCanvas"
            Grid.Column="1"
            Width="378"
            Height="378"
            Background="#FF008000" />
    </Grid>

</ContentDialog>


  public sealed partial class GuidedProcessDialog : ContentDialog {
      MainViewModel? ViewModel { get; set; }
      public GuidedProcessDialog(MainViewModel ViewModel) {
          InitializeComponent();
          DataContext = ViewModel;
      }
  }

DI

        public static ServiceProvider? Services { get; private set; }
        private const string SyncfusionLicense = "MzczMDcxNEAzMjM4MmUzMDJlMzBrV3dmVEhRTFFJRmtZRGZUdzRqeEJmN3lOcU5wUkwzb2tBRnRuOVNyOHJzPQ==";
        public App() {
            SyncfusionLicenseProvider.RegisterLicense(SyncfusionLicense);
            var services = new ServiceCollection();
            services.AddSingleton<MainViewModel>();
            services.AddTransient(p =>
                                 new MainWindow(
                                     p.GetRequiredService<MainViewModel>()));
            Services = services.BuildServiceProvider();
            InitializeComponent();
        }
        protected override void OnLaunched(LaunchActivatedEventArgs args) {
            var window = Services!.GetRequiredService<MainWindow>();
            window.Activate();
            var hwnd = WindowNative.GetWindowHandle(window);
            var windowId = Win32Interop.GetWindowIdFromWindow(hwnd);
            var appWindow = AppWindow.GetFromWindowId(windowId);
            var presenter = appWindow.Presenter as OverlappedPresenter;
            presenter?.Maximize();
        }
    }
}
Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,741 questions
0 comments No comments
{count} votes

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.