Как реализовать сохранение пользователем разметки XAML?

Всем привет!

Проект WPF.

Собственно, хочу узнать как лучше(и можно ли вообще) такое реализовать. Т.е, например, пользователь по нажатию кнопки добавляет текстовое поле и при следующем запуске программы это поле уже будет в окне.

Были мысли в сторону того, чтобы сохранять разметку в файл xml, а потом подставлять ее в разметку xaml, но не нашел как.

Заранее спасибо!


Ответы (1 шт):

Автор решения: aepot

XAML разметка - это нечно статичное, то что вы создаете при реализации приложения. Она не изменяется во время его работы. Конечно, техническая возможность распарсить XAML и загрузить из него интерфейс есть, но в этом нет никакого практического смысла, к тому же это не просто.

Но если вам интересно, как именно загружается XAML разметка, поставьте курсор на InitializeComponent() и нажмите F12, увидите исходный код загрузчика окна.

Если вам нужна куча текстбоксов, а данные для текстбокса - это строка, то вам нужна коллекция строк. Есть в дотнете особая коллекция ObservableCollection, состав которой интерфейс умеет автоматически отслеживать. То есть вы создаете в ней элемент, и интерфейс тут же реагирует на эти изменения. И есть простой контрол, который умеет работать с такими коллекциями - ItemsControl. Чтобы их подружить вместе, нужно выполнить привязку данных коллекции к этому контролу. За это отвечает Binding.

Привязка возможна только к публичным свойствам. Давайте создадим такое свойство, например прямо в классе MainWindow.

public ObservableCollection<string> Items { get; } = new ObservableCollection<string>();

Далее в XAML разметке нужно реализовать вот такую конструкцию

<ItemsControl ItemsSource="{Binding Items}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBox Text="{Binding}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

В таком виде работать не будет, а чтобы заработало, надо в конструкторе окна назначить DataContext, то есть указать место, где Binding должен искать данные.

InitializeComponent();
DataContext = this;

Вот теперь все работает. Например если вы захотите добавить элемент, надо просто сделать вот так.

Items.Add(string.Empty);

И тут же появится текстбокс.

Чтобы сохранить данные, можно использовать сериализацию коллекции, например в JSON.

File.WriteAllText("file.json", JsonSerializer.Serialize(Items));

А вот чтобы загрузить, есть нюанс, без реализации INotifyPropertyChanged (это отдельная история, но копать вам надо именно туда), присваивание Items можно делать только в конструкторе, в противном случае привязка будет разорвана, и интерфейс просто потеряет данные. Поэтому придется добавлять элементы, а не пересоздавать коллекцию.

Items.Clear();
string[] items = JsonSerializer.Deserialize<string[]>(File.ReadAllText("file.json"));
foreach (string item in items)
{
    Items.Add(item);
}

Если это надо делать при загрузке приложения, то лучше этот код разместить в обработчике события Window.Loaded. Не пишите в конструкторе окна свой код, ну кроме того что я выше про DataContext указал.

Это очень сильно упрощенный пример, чтобы двигаться дальше, вам нужно научиться создавать модели данных (классы для данных) и познакомиться с INotifyPropertyChanged. А чтобы эти все знания научиться правильно применять - изучите шаблон проектирования MVVM.

→ Ссылка