私は 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 で定義された値に戻ります:(
だから私はこれに対する解決策を求めてインターネットを精査するのにかなりの時間を費やしました、そして私が集めたものから、これはデザイナーがソース(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 バッカーを変更することで処理されますが、これを達成する方法に関するドキュメントは見つかりません.
さらに読む
と、私の例がレイアウトを変更していることに気付きました。これを達成する方法は他にもあります (レイアウト システム (arrangeOverride と measureOverride) を使用するなど) ですが、このアプローチでは必要な種類のコントロールが得られません。
また、装飾ツリーを鳴らして、UI のないカスタム装飾を作成しましたが、パネルの onPropertyChanged イベントをフックし、子の幅を変更しました。装飾者は ModelItem を手に入れることができるため、これは実際に機能しますが、限られた方法でしか機能しません。adorner ルートでは、メソッドは Cider (visual studio 2010) では完全に機能しますが、Blend では半分しか機能しません。Blend では、プロパティ エディタでパネルの幅が変更されると、子が更新されます。ただし、デザイン サーフェイスの幅ハンドルがブレンドでドラッグされた場合、子は更新されません (ビジュアル スタジオでは、デザイン サーフェイス ハンドルが使用されると子は更新されます)。
adornerのコードははるかに複雑ですが、チュートリアル: デザイン時の Adorner の作成(MSDN を検索してください。リンクは 1 つしか投稿できません) に従います。 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;
}
}
}