עזרה בקוד C# wpf
-
עשיתי קוד שיוצר תכונת snap על ידי כפתורים דינמיים עבור usercontrol בwpf
נתקלתי בבעיה שבפעם הרשונה שהכפתורים נטענים המיקום שלהם משובש.מצו:ב הקוד
<UserControl x:Class="test_wpf_snap.UserControl3" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" MouseLeftButtonDown="UserControl_MouseLeftButtonDown" MouseMove="UserControl_MouseMove" MouseLeftButtonUp="UserControl_MouseLeftButtonUp" BorderBrush="Black" d:DesignHeight="100" d:DesignWidth="100"> <UserControl.Background> <SolidColorBrush Color="{DynamicResource {x:Static SystemColors.ControlColorKey}}"/> </UserControl.Background> <Grid Width="100" Height="100"> <Grid.RowDefinitions> <RowDefinition Height="25"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <ToolBar Grid.Row="0"> </ToolBar> <DockPanel > <DockPanel.Background> <SolidColorBrush Color="{DynamicResource {x:Static SystemColors.WindowColorKey}}"/> </DockPanel.Background> <Button Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" Content="X" Width="25" DockPanel.Dock="Right" HorizontalAlignment="Right"/> </DockPanel> </Grid> </UserControl>
using System; using System.Collections.Generic; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; namespace test_wpf_snap { /// <summary> /// Interaction logic for UserControl3.xaml /// </summary> public partial class UserControl3 : UserControl { private bool inDrag = false; private Point anchorPoint; private bool showSnapButtons = false; SnapButtons snapButtons; public UserControl3() { InitializeComponent(); snapButtons = new SnapButtons(this); } private void UserControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (!inDrag) { anchorPoint = e.GetPosition(null); CaptureMouse(); inDrag = true; snapButtons.ShowSnapButtons(); e.Handled = true; } } private void UserControl_MouseMove(object sender, MouseEventArgs e) { if (inDrag) { Point currentPoint = e.GetPosition(null); // Move the UserControl Canvas.SetLeft(this, Canvas.GetLeft(this) + (currentPoint.X - anchorPoint.X)); Canvas.SetTop(this, Canvas.GetTop(this) + (currentPoint.Y - anchorPoint.Y)); anchorPoint = currentPoint; snapButtons.HighlightSnapButtonIfMouseOver(snapButtons.snapTopButton, e); snapButtons.HighlightSnapButtonIfMouseOver(snapButtons.snapBottomButton, e); snapButtons.HighlightSnapButtonIfMouseOver(snapButtons.snapLeftButton, e); snapButtons.HighlightSnapButtonIfMouseOver(snapButtons.snapRightButton, e); } e.Handled = true; } private void UserControl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (inDrag) { ReleaseMouseCapture(); inDrag = false; snapButtons.HideSnapButtons(); e.Handled = true; } } } }
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Threading; namespace test_wpf_snap { public class SnapButtons { public Button snapTopButton; public Button snapBottomButton; public Button snapLeftButton; public Button snapRightButton; private UserControl snapControl; private bool showSnapButtons; public SnapButtons(UserControl control) { InitializeSnapButtons(); snapControl = control; } private void InitializeSnapButtons() { snapTopButton = CreateSnapButton("Snap\r\nTop", "SnapToTop"); snapBottomButton = CreateSnapButton("Snap\r\nBottom", "SnapToBottom"); snapLeftButton = CreateSnapButton("Snap\r\nLeft", "SnapToLeft"); snapRightButton = CreateSnapButton("Snap\r\nRight", "SnapToRight"); HideSnapButtons(); } private Button CreateSnapButton(string buttonText, string tag) { var button = new Button { Content = CreateCenteredTextBlock(buttonText), Tag = tag }; SetButtonStyle(button); return button; } private TextBlock CreateCenteredTextBlock(string text) { var textBlock = new TextBlock { FontSize = 9, Text = text, TextAlignment = TextAlignment.Center, VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Stretch }; return textBlock; } private void SetButtonStyle(Button button) { button.Background = Brushes.Transparent; button.Foreground = Brushes.Black; button.Width = 30; button.Height = 30; button.MouseEnter += (sender, e) => SnapToPosition((Button)sender); } private void SnapToPosition(Button button) { double targetLeft = Canvas.GetLeft(snapControl); double targetTop = Canvas.GetTop(snapControl); switch (button.Tag.ToString()) { case "SnapToTop": targetTop = 0; break; case "SnapToBottom": targetTop = ((Canvas)snapControl.Parent).ActualHeight - snapControl.ActualHeight; break; case "SnapToLeft": targetLeft = 0; break; case "SnapToRight": targetLeft = ((Canvas)snapControl.Parent).ActualWidth - snapControl.ActualWidth; break; } Canvas.SetLeft(snapControl, targetLeft); Canvas.SetTop(snapControl, targetTop); } public void HighlightSnapButtonIfMouseOver(Button button, MouseEventArgs e) { Point mousePosition = e.GetPosition(button); if (mousePosition.X >= 0 && mousePosition.X <= button.ActualWidth && mousePosition.Y >= 0 && mousePosition.Y <= button.ActualHeight) { // Use system default highlight color button.Background = SystemColors.ActiveCaptionBrush; } else { // Remove highlighting if the mouse is not over the button button.Background = Brushes.Transparent; } } public void ShowSnapButtons() { if (showSnapButtons) return; Canvas canvas = snapControl.Parent as Canvas; if (canvas == null) return; double centerX = Canvas.GetLeft(snapControl) + snapControl.ActualWidth / 2; double centerY = Canvas.GetTop(snapControl) + snapControl.ActualHeight / 2; double buttonSeparation = 10; // Set the position of the imaginary center button SetSnapButtonPosition(snapBottomButton, centerX, centerY); // Adjust other buttons relative to the imaginary center button SetSnapButtonPosition(snapTopButton, centerX, centerY - snapTopButton.ActualHeight - buttonSeparation); SetSnapButtonPosition(snapLeftButton, centerX - snapLeftButton.ActualWidth - buttonSeparation, centerY); SetSnapButtonPosition(snapRightButton, centerX + snapBottomButton.ActualWidth + buttonSeparation, centerY); // Apply styles and add buttons to the canvas foreach (Button button in new[] { snapTopButton, snapBottomButton, snapLeftButton, snapRightButton }) { canvas.Children.Add(button); } showSnapButtons = true; } private void SetSnapButtonPosition(Button button, double left, double top) { Canvas.SetLeft(button, left - button.ActualWidth / 2); Canvas.SetTop(button, top - button.ActualHeight / 2); } public void HideSnapButtons() { if (showSnapButtons) { // Remove snap buttons from the Canvas Canvas canvas = snapControl.Parent as Canvas; canvas?.Children.Remove(snapTopButton); canvas?.Children.Remove(snapBottomButton); canvas?.Children.Remove(snapLeftButton); canvas?.Children.Remove(snapRightButton); showSnapButtons = false; } } } }
-
@dovid
לא הסברתי את שאלתי בצורה מספיק ברורה. סליחה.
בקוד הנ"ל הוספתי תכונה ליוזר פורם שאפשר להוזיז אותו (זה נמצא בclass של היוזרפורם).
ועוד תכונה (בclass נפרד) שכאשר המשתמש מתחיל להזיז את היוזרפורם אז מופיעים ארבעה כפתורים באיזור של העכבר (התפקיד של הכפתורים הוא להצמיד את היוזר פורם לאחד מן הצדדים של החלון המרכזי אם המתשמש עוזב את העכבר כאשר הוא מעל אחד מהכפתורים).
המראה של הכפתורים אמור להיות ככה:
אבל זה משתבש לי בפעם הראשונה שמשתמש לוחץ על היוזר פורם
כמו שאתה רואים הכפתורים לא מופיעים כמו שרציתי. - משום מה זה קורה רק בפעם הראשונה ואני לא מצליח להבין למה. -
אוקי, הקוד לימדני ש: הפקד צריך להיות בCanvas + שצריך להיות מוגדר לו Top וגם Left.
<Canvas> <test_wpf_snap:UserControl3 Canvas.Left="150" Canvas.Top="150" /> </Canvas>
אוקי. מממ. הבעיה היא שActualHeight של כל פקדי הsnap הם 0 כל עוד הם לא רונדרו על המסך פעם ראשונה.
זה בעיה מפורסמת של ActualHeight, הוא מחושבן רק אחרי שמציאותית הפקד נראה וזה קורה שבריר שניה אחרי כל קוד שירוץ (כלומר רק אחרי Delay כל שהוא).
אבל אין בכלל פה סיבה לשימוש בActualHeight וגם לכאורה כל השימוש בקוד ליצירת ויזואל זה מאוד מאוד לא WPF'דיק.
הדרך הישרה היא לעשות פקד XAML שמייצג את הSNAP, והוא גם אחראי למראה שלו ולאירועים וכולי, וככה אין קוד C# שמעצב, שזה ממש לא נעים למתכנת WPF לראות!
כיון שזה מורכב (כמו כל דבר בWPF) אז לבינתיים הלכתי בדכך ורק שיפרתי את ההתנהגות לפתרון הבעיות. זה הקוד:using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; namespace test_wpf_snap { public partial class UserControl3 : UserControl { public UserControl3() { InitializeComponent(); new SnapButtons(this); } } public class SnapButtons { private Point anchorPoint; private bool inDrag = false; private UserControl snapControl; Grid Grid; Button[] buttons; public SnapButtons(UserControl control) { snapControl = control; snapControl.MouseMove += UserControl_MouseMove; snapControl.MouseLeftButtonDown += UserControl_MouseLeftButtonDown; snapControl.MouseLeftButtonUp += UserControl_MouseLeftButtonUp; } private Grid CreateGrid() { var grid = new Grid(); var btnStyle = new Style(typeof(Button)); btnStyle.Setters.Add(new Setter(Control.ForegroundProperty, Brushes.Black)); btnStyle.Setters.Add(new Setter(Control.BackgroundProperty, Brushes.Transparent)); btnStyle.Setters.Add(new Setter(Control.PaddingProperty, new Thickness(10))); buttons = [ new Button { Content = "Top", Tag = "SnapToTop", HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Top, Style = btnStyle }, new Button { Content = "Bottom", Tag = "SnapToBottom", HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Bottom, Style = btnStyle }, new Button { Content = "Left", Tag = "SnapToLeft", HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Center, Style = btnStyle }, new Button { Content = "Right", Tag = "SnapToRight", HorizontalAlignment = HorizontalAlignment.Right, VerticalAlignment = VerticalAlignment.Center, Style = btnStyle } ]; foreach (var btn in buttons) { btn.MouseEnter += (sender, e) => SnapToPosition((Button)sender); Grid.Children.Add(btn); } Grid.Height = 100; Grid.Width = 100; return Grid; } public void HighlightSnapButtonIfMouseOver(MouseEventArgs e) { foreach (Button button in buttons) { Point mousePosition = e.GetPosition(button); if (mousePosition.X >= 0 && mousePosition.X <= button.ActualWidth && mousePosition.Y >= 0 && mousePosition.Y <= button.ActualHeight) { // Use system default highlight color button.Background = SystemColors.ActiveCaptionBrush; } else { // Remove highlighting if the mouse is not over the button button.Background = Brushes.Transparent; } } } private void UserControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (!inDrag) { anchorPoint = e.GetPosition(null); snapControl.CaptureMouse(); inDrag = true; ShowSnapButtons(); e.Handled = true; } } private void UserControl_MouseMove(object sender, MouseEventArgs e) { if (inDrag) { Point currentPoint = e.GetPosition(null); // Move the UserControl Canvas.SetLeft(snapControl, Canvas.GetLeft(snapControl) + (currentPoint.X - anchorPoint.X)); Canvas.SetTop(snapControl, Canvas.GetTop(snapControl) + (currentPoint.Y - anchorPoint.Y)); anchorPoint = currentPoint; HighlightSnapButtonIfMouseOver(e); } e.Handled = true; } private void UserControl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (inDrag) { snapControl.ReleaseMouseCapture(); inDrag = false; HideSnapButtons(); e.Handled = true; } } private void SnapToPosition(Button button) { double targetLeft = Canvas.GetLeft(snapControl); double targetTop = Canvas.GetTop(snapControl); switch (button.Tag.ToString()) { case "SnapToTop": targetTop = 0; break; case "SnapToBottom": targetTop = ((Canvas)snapControl.Parent).ActualHeight - snapControl.ActualHeight; break; case "SnapToLeft": targetLeft = 0; break; case "SnapToRight": targetLeft = ((Canvas)snapControl.Parent).ActualWidth - snapControl.ActualWidth; break; } Canvas.SetLeft(snapControl, targetLeft); Canvas.SetTop(snapControl, targetTop); } public void ShowSnapButtons() { Canvas? canvas = snapControl.Parent as Canvas; if (canvas == null) return; if (Grid == null) { Grid = CreateGrid(); canvas.Children.Add(Grid); } Grid.Visibility = Visibility.Visible; Canvas.SetLeft(Grid, Math.Min(anchorPoint.X, canvas.ActualWidth - Grid.Width)); Canvas.SetTop(Grid, Math.Min(anchorPoint.Y, canvas.ActualHeight - Grid.Height)); } public void HideSnapButtons() { Grid.Visibility = Visibility.Collapsed; } } }
<UserControl x:Class="test_wpf_snap.UserControl3" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" BorderBrush="Black" d:DesignHeight="100" d:DesignWidth="100"> <UserControl.Background> <SolidColorBrush Color="{DynamicResource {x:Static SystemColors.ControlColorKey}}"/> </UserControl.Background> <Grid Width="100" Height="100"> <Grid.RowDefinitions> <RowDefinition Height="25"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <ToolBar Grid.Row="0"> </ToolBar> <DockPanel > <DockPanel.Background> <SolidColorBrush Color="{DynamicResource {x:Static SystemColors.WindowColorKey}}"/> </DockPanel.Background> <Button Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" Content="X" Width="25" DockPanel.Dock="Right" HorizontalAlignment="Right"/> </DockPanel> </Grid> </UserControl>