Координаты круга с сохранением в файл в wpf
Не получается сохранить координаты круга. Суть игры найти круг по звуку. Задание - пользователь может загрузить потом игру где остановился.
private void Save(object sender, RoutedEventArgs e)
{
StreamWriter writer = new StreamWriter(@"C:\soh.txt"); //указываем путь к файлу.
double x = MainCanvas.GetLeft(target);
double y = MainCanvas.GetTop(target);
/* foreach (Ellipse rect in MainCanvas.Children.OfType<Ellipse>())
{
double x = Canvas.GetLeft(rect);
double y = Canvas.GetTop(rect);
}*/
writer.WriteLine(x);
writer.WriteLine(y);
writer.Close();
}
Это мои попытки, а это класс точки:
Ellipse target; // круг
Canvas maincanvas; // на котором рисуется точка
SineWaveProvider32 sineWaveProvider = new SineWaveProvider32();
public Target(Point center, double radius, Canvas canvas) // конструктор класса точки
{
target = new Ellipse();
target.Width = radius * 2;
target.Height = radius * 2;
Canvas.SetLeft(target, center.X - radius);
Canvas.SetTop(target, center.Y - radius);
target.Fill = new SolidColorBrush(Color.FromArgb(0, 0, 0, 0));
sineWaveProvider.SetWaveFormat(16000, 1); // 16kHz mono
sineWaveProvider.Frequency = 500;
sineWaveProvider.Amplitude = 0.1f;
waveOut = new WaveOut();
waveOut.Init(sineWaveProvider);
waveOut.Play();
maincanvas = canvas;
canvas.Children.Add(target);
Panel.SetZIndex(target, 100);
}
Если раскоментить, то не сохраняется в файл, если так оставить, то пишет ошибку, что невозможно преобразовать в UI элемент :(
Ответы (1 шт):
Вы неправильно используете контролы в WPF: считается очень сложной ручная работа с контрлами из кода, и требуется она обычно только в каких-то особых случаях. Во всех остальных случаях разработчики используют привязки данных Binding.
Ниже я покажу пример, как это может работать.
Пример содержит:
INotifyPropertyChanged- реализация этого интерфейса позволяет уведомлять привязанные контролы об изменениях в свойствах с помощью методаOnPropertyChanged().Binding- привязки данных в XAML.IValueConverter- нужен когда свойство содержит один тип данных, а контролу нужен другой. В данном случае свойство будет типаbool, а контролу нужноVisibility.
Я сделал Canvas размером с окно, поместил в него Ellipse, установил DataContext (это важно) и реализовал INotifyPropertyChanged. Далее добавил в класс все необходимые свойства, а в XAML разметке просто к ним привязался.
Создайте пустой WPF проект, перенесите в него разметку и код, и посмотрите, как это работает. Обязательно разберите, что здесь к чему, это даст вам новые возможности и сделает во много раз легче работу с WPF.
Конвертер, используется в XAML разметке
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
=> value is bool v && v ? Visibility.Visible : Visibility.Collapsed;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> value is Visibility v && v == Visibility.Visible ? true : false;
}
Код приложения
public partial class MainWindow : Window, INotifyPropertyChanged
{
private double _circleX;
private double _circleY;
private double _circleRadius;
private bool _circleVisibile;
private string _status;
public double CircleX
{
get => _circleX;
set
{
_circleX = value;
OnPropertyChanged();
}
}
public double CircleY
{
get => _circleY;
set
{
_circleY = value;
OnPropertyChanged();
}
}
public double CircleRadius
{
get => _circleRadius;
set
{
_circleRadius = value;
OnPropertyChanged();
}
}
public bool CircleVisible
{
get => _circleVisibile;
set
{
_circleVisibile = value;
OnPropertyChanged();
}
}
public string Status
{
get => _status;
set
{
_status = value;
OnPropertyChanged();
}
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
CircleX = 100;
CircleY = 100;
CircleRadius = 50;
CircleVisible = true;
}
const string _fileName = "file.txt";
private void SaveButton_Click(object sender, RoutedEventArgs e)
{
try
{
using (StreamWriter sw = new StreamWriter(_fileName))
{
sw.WriteLine(CircleX);
sw.WriteLine(CircleY);
sw.WriteLine(CircleRadius);
sw.WriteLine(CircleVisible);
}
Status = "Файл успешно записан";
}
catch (Exception ex)
{
Status = "Ошибка записи в файл: " +ex.Message;
}
}
private void LoadButton_Click(object sender, RoutedEventArgs e)
{
try
{
using (StreamReader sr = new StreamReader(_fileName))
{
CircleX = double.Parse(sr.ReadLine());
CircleY = double.Parse(sr.ReadLine());
CircleRadius = double.Parse(sr.ReadLine());
CircleVisible = bool.Parse(sr.ReadLine());
}
Status = "Файл успешно прочитан";
}
catch (Exception ex)
{
Status = "Ошибка чтения из файла: " + ex.Message;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Полная разметка
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Width="800" Height="500" Loaded="Window_Loaded">
<Window.Resources>
<local:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Margin" Value="5"/>
<Setter Property="Width" Value="120"/>
</Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="5"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Margin" Value="5"/>
<Setter Property="Padding" Value="5,0"/>
</Style>
</StackPanel.Resources>
<TextBlock Text="Visible:"/>
<CheckBox Margin="5" VerticalAlignment="Center" IsChecked="{Binding CircleVisible}"/>
<TextBlock Text="X:"/>
<TextBox Text="{Binding CircleX, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text="Y:"/>
<TextBox Text="{Binding CircleY, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Text="Radius:"/>
<TextBox Text="{Binding CircleRadius, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="Save" Click="SaveButton_Click"/>
<Button Content="Load" Click="LoadButton_Click"/>
</StackPanel>
<Canvas Grid.Row="1">
<Ellipse Width="{Binding CircleRadius}" Height="{Binding CircleRadius}" Canvas.Left="{Binding CircleX}" Canvas.Top="{Binding CircleY}" Visibility="{Binding CircleVisible, Converter={StaticResource BoolToVisibilityConverter}}" Stroke="Black" StrokeThickness="1"/>
</Canvas>
<StatusBar Grid.Row="2">
<TextBlock Margin="5" Text="{Binding Status}"/>
</StatusBar>
</Grid>
</Window>
Содержимое сохраненного файла выглядит вот так
100
100
50
True
Интерфейс выглядит вот так
Просто изменяйте значения в текстбоксах и жмите кнопки и чекбокс, круг будет реагировать на эти изменения в режиме реального времени. Обратите внимание, ни одному контролу не дал имя, и не обращаюсь к контролам из кода, совсем.
