Изменение свойства во время разработки не обновляет XAML в Expression Blend 4 - PullRequest
2 голосов
/ 13 ноября 2010

Я работал над настраиваемой панелью для WPF и столкнулся с проблемой с некоторым кодом времени разработки. Чтобы свести к минимуму проблему, если у меня есть код, выполняющийся во время разработки, и этот код изменяет свойство некоторого объекта (либо панели, либо дочернего элемента панели), в конструкторе я вижу соответствующее изменение, но XAML это составляет окно, ничего не обновляется.

пример:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;

namespace StackOverflowExample
{
    class MyPanel : Canvas
    {
        public MyPanel()
        {
            this.SizeChanged += new System.Windows.SizeChangedEventHandler(MyPanel_SizeChanged);
        }

        void MyPanel_SizeChanged(object sender, System.Windows.SizeChangedEventArgs e)
        {
            foreach (FrameworkElement child in this.Children)
            {
                if (child != null)
                {
                    double widthDelta = e.NewSize.Width - e.PreviousSize.Width;
                    double newWidth = Math.Max(0.0, child.Width + widthDelta);
                    child.Width = newWidth;
                }
            }
        }
    }
}

Если вы создаете новое приложение WPF и добавляете этот класс, то создайте окно с этим XAML:

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:StackOverflowExample" x:Class="StackOverflowExample.MainWindow"
        Title="MainWindow" Height="350" Width="525">
    <Canvas>

        <local:MyPanel Height="153" Canvas.Left="108" Canvas.Top="43" Width="278">
            <Rectangle Fill="#FFF4F4F5" Height="77" Canvas.Left="43" Stroke="Black" Canvas.Top="37" Width="87"/>
        </local:MyPanel>

    </Canvas>
</Window>

Если вы затем измените ширину панели (перетаскивая ручку ширины в конструкторе или изменив свойство ширины в редакторе свойств), прямоугольник внутри будет корректно обновляться в области конструктора. Однако если вы посмотрите на XAML, обновится только ширина панели, ширина прямоугольника останется неизменной. Если вы строите проект после изменения ширины панели, прямоугольник возвращается к значению, определенному в XAML: (

Так что я потратил много времени на поиски решений в Интернете, и, как я понял, это связано с тем, что дизайнеры имеют представление о Source (XAML), Представление (фактическая вещь в конструкторе, с которым вы играете) и Экземпляр (создание экземпляра в памяти объекта, определенного классом ModelItem). Это Экземпляр, который нужно изменить, чтобы иметь обновление XAML из того, что я собираю. Однако мне не повезло в получении экземпляра ModelInstance.

Это сообщение Вело меня в направлении, чтобы попробовать что-то вроде этого:

...
if (child != null)
{
    double widthDelta = e.NewSize.Width - e.PreviousSize.Width;
    double newWidth = Math.Max(0.0, child.Width + widthDelta);

    EditingContext ec = new EditingContext();
    ModelTreeManager mtm = new ModelTreeManager(ec);
    System.Activities.Presentation.Model.ModelItem model = mtm.CreateModelItem(null, child);
    model.Properties["Width"].SetValue(newWidth);
}

Однако, это просто приводит к сбою Blend ... Я прошел этот код в отладчике, и он говорит мне, что сбой является исключением nullRefferenceException, но неясно, что такое NULL. Я считаю, что это корневое свойство modelTreeManager, но если это так, я понятия не имею, как его правильно установить.

Так что то, что я пытаюсь сделать, кажется достаточно простым. Измените свойство чего-либо во время разработки и сделайте так, чтобы изменение сериализовалось в XAML ... Предположительно, это выполняется путем изменения бэкера ModelItem элемента в конструкторе, однако я не могу найти никакой документации о том, как это сделать. .

Дополнительная литература
Я понимаю, что мой пример - это изменение макета, и для этого есть другие способы (например, использование системы макетов (rangeOverride и measureOverride), однако этот подход не дает мне нужный мне тип элемента управления.

Кроме того, я окрикнул дерево рекламного устройства, создав пользовательский рекламный баннер, у которого не было пользовательского интерфейса, но было подключено событие onPropertyChanged панели и изменена ширина дочерних элементов. Это на самом деле работает, так как владелец может получить доступ к ModelItem, но он работает только ограниченным образом. С маршрутом Adorner, метод работает ОТЛИЧНО в Cider (visual studio 2010), но в Blend работает только наполовину. В blend, если ширина панели изменяется в редакторе свойств, дочерние элементы обновляются. Однако, если указатель ширины на поверхности дизайна перетаскивается в смешение, дочерние элементы НЕ обновляются (в визуальной студии дочерние элементы обновляются при использовании ручки дизайнерской поверхности).

Код для Adorner гораздо сложнее, но следует Пошаговое руководство. Создание Adorner во время разработки (поиск по MSDN, я могу опубликовать только одну ссылку), измененное для работы с обоими дизайнерами (используя MyAssembly. Design.dll вместо MyAssembly.VisualStuido.Design.dll). Возможно, стоит отметить, что ползунок непрозрачности обновляется в режиме реального времени (как он перетаскивается) в Cider, но обновляется только при выпуске мыши в Blend. Вот код моего поклонника, если кому-то интересно.

class MyPanelAdornerProvider : PrimarySelectionAdornerProvider
{
    private ModelItem adornedControlModel;
    private double previousWidth;

    protected override void Activate(Microsoft.Windows.Design.Model.ModelItem item)
    {
        adornedControlModel = item;
        adornedControlModel.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(AdornedControlModel_PropertyChanged);
        previousWidth = (double)adornedControlModel.Properties["Width"].ComputedValue;

        base.Activate(item);
    }

    protected override void Deactivate()
    {
        adornedControlModel.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler(AdornedControlModel_PropertyChanged);

        base.Deactivate();
    }

    void AdornedControlModel_PropertyChanged( object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "Width")
        {
            double widthDelta = ((double)adornedControlModel.Properties["Width"].ComputedValue) - previousWidth;
            ModelItemCollection children = adornedControlModel.Properties["Children"].Collection;

            foreach (ModelItem item in children)
            {
                item.Properties["Width"].SetValue(Math.Max(0.0, ((double)item.Properties["Width"].ComputedValue) + widthDelta));
            }

            previousWidth = (double)adornedControlModel.Properties["Width"].ComputedValue;
        }
    }
}
...