Wpf

WPFカスタムコントロール-デコレータ



Wpf Custom Control Decorator



名前が示すように、装飾的です。つまり、元のコントロール構造は変更されませんが、コントロールにいくつかの新しい機能を追加したり、コントロールの表示外観に何かを追加したりできます。 MSDNの例のように:

無題



元々、TextBoxの隅にドットはありませんが、デコレータで追加できます。したがって、TextBoxにレイヤーを追加することを考えることができます。

画像



これは、コントロールを「痛みのない」装飾しました。もちろん、アプリケーションは、ドラッグコントロールの変更のように、ほんの数ポイントではありません。

画像

画像



以前に有名なレイヤードラッグはBeaStollinitzです。 データバインドされたItemsControls間でアイテムをドラッグアンドドロップするにはどうすればよいですか?

InsertionAdorner.png

1。 AdornerLayer

レイヤーは、コントロールをカバーするもののレイヤーであると言います。コントロールは複数のレイヤーをカバーできますか?

画像

答えはもちろん大丈夫です、そして層は自然に呼ばれるコンテナに置かれます AdornerLayer

それから質問が再び来ました。この層はどのようにして生まれたのですか?それは人為的にリリースされたものですか、それとも自動的に生成されたものですか(実際には誰かがそれを書く必要がありますが)?

私たちは知っています AdornerLayer やり方がある

public static AdornerLayer GetAdornerLayer(Visual visual)

ビジュアルが配置されているレイヤーを取得できます。Reflectorを開いて表示します

public static AdornerLayer GetAdornerLayer(Visual visual) { if (visual == null) { throw new ArgumentNullException('visual') } for (Visual visual2 = VisualTreeHelper.GetParent(visual) as Visual visual2 != null visual2 = VisualTreeHelper.GetParent(visual2) as Visual) { if (visual2 is AdornerDecorator) { return ((AdornerDecorator)visual2).AdornerLayer } if (visual2 is ScrollContentPresenter) { return ((ScrollContentPresenter)visual2).AdornerLayer } } return null }

実際にビジュアルツリーを調べて、要素がAdornerDecoratorまたはScrollContentPresenterはいの場合、AdornerLayerプロパティを取得します。これは、AdornerLayerがAdornerDecoratorまたはScrollContentPresenter生成され、ローカルのMSDNを開き、次のように入力しますScrollContentPresenter

画像

赤いボックスのテキストから知ることができますScrollContentPresenterScrollViewerに属します。つまり、ScrollViewerがある場所にAdornerLayerがあります。 ScrollViewerリンクを開くと、ScrollViewerが通常必要とすることが理解できます 包装パネルコントロール

画像

したがって、私が知る限り、どのコントロールのデフォルトスタイルがScrollViewerで使用され、ItemsControlコントロール、およびもちろんここにリストされていないWindowsやその他の一般的に使用されるコントロールから継承されます。

ScrollViewerの場所がない場合、またはコントロールに直接レイヤーを追加する必要がある場合は、手動でコントロールを削除することもできます。AdornerDecoratorAdornerLayerを作成します。

<AdornerDecorator> <TextBox Text='You can get AdornerLayer'/> AdornerDecorator>

では、AdornerLayerの概念は何ですか、なぜそれが常に制御されているのですか?

リフレクターで開くScrollContentPresenterまたはAdornerDecoratorGetVisualChild (int index)は次のコードに注意する必要があります(以下のコードはScrollContentPresenter得られた)

private readonly AdornerLayer _adornerLayer = new AdornerLayer() protected override Visual GetVisualChild(int index) { if (base.TemplateChild == null) { throw new ArgumentOutOfRangeException('index', index, SR.Get('Visual_ArgumentOutOfRange')) } switch (index) { case 0: return base.TemplateChild case 1: return this._adornerLayer } throw new ArgumentOutOfRangeException('index', index, SR.Get('Visual_ArgumentOutOfRange')) }

ここで、インデックス0は通常、装飾する必要のあるコントロールであり、1はAdornerLayerであることは明らかです。システムは最初に0レイヤーを描画し、次に1レイヤーを描画するため、常に1が0になります。したがって、実際には、AdornerLayerもビジュアルツリーに存在し、渡すことができます。VisualTreeHelper見つけに来てください。そして、あなたはコントロールのz-indexが役に立たないかどうかを気にしません、人々は死んで書いています。

二。アドナー

コンテナでは、物を追加するのが自然です。さもないと、空ではなく、何もありません。また、AdornerLayerは、このコンテナーにのみ追加できるのはAdornerの派生クラスであると規定しています。この傲慢さの下で、私たちはAdornerの抽象クラスからクラスを放棄して継承する必要があります。

public class SimpleTextBlockAdorner : Adorner { private TextBlock _textBlock public SimpleTextBlockAdorner(UIElement adornedElement) : base(adornedElement) { _textBlock = new TextBlock() _textBlock.Foreground = Brushes.Green _textBlock.Text = 'AdornerText' } protected override Visual GetVisualChild(int index) { return _textBlock } protected override int VisualChildrenCount { get { return 1 } } protected override Size ArrangeOverride(Size finalSize) { / / Specify the position and size of the control _textBlock.Arrange(new Rect(new Point(-10, 20), _textBlock.DesiredSize)) return base.ArrangeOverride(finalSize) } }

CSのコードは次のとおりです。

public Window1() { InitializeComponent() TextBlock textBlock = new TextBlock() textBlock.FlowDirection = FlowDirection.RightToLeft textBlock.Text = 'FlowDirection.RightToLeft' / / Put a layer container AdornerDecorator adornerDecorator = new AdornerDecorator() adornerDecorator.Child = textBlock this.Content = adornerDecorator / / Get the layer container var adornerLayer = AdornerLayer.GetAdornerLayer(textBlock) / / Add layers in the layer container adornerLayer.Add(new SimpleTextBlockAdorner(textBlock)) }

textBlockのFlowDirectionプロパティを変更すると、次のような結果が表示されます。つまり、装飾された要素のFlowDirection効果は、レイヤー上の要素に影響を与えます。

画像 画像

それを探求して、私は再びReflectorを使わなければなりません。

画像

拘束力があることがわかりましたが、どのシーンが適用されるかというDispatcherPriorityの順序の意味がわかりませんでしたので、専門家の方のご指導をお願いします。

次に、私たちが気にかけているもう1つのことは、このレイヤーの大きさです。装飾されたコントロール全体をカバーしているのか、画面全体をカバーしているのか、ウィンドウ全体をカバーしているのか。通常、MeasureOverrideはコントロールのサイズを指定でき、順序はMeasure-> Arrange-> Renderです(画像をレイアウトに貼り付けたいのですが、実際には間違っている場所を振り返ることができます。時間、RZ)。では、MeasureOverrideの機能を見てみましょう。

画像

赤い線のボックスから、彼が装飾的なコントロールを使用していることが簡単にわかります プレゼンテーションサイズ 変更します。もちろん、これはデフォルトにすぎません。MeasureOverrideをオーバーライドしてサイズを指定することもできます。

しかし、デコレーションコントロール(この記事のTextBox)のサイズが変更された場合、デコレーター(この記事のSimpleTextBlockAdorner)はどのようにそれを検出しますか?

実際、装飾コントロールのサイズを変更する場合、コントロールの高さまたは幅を変更することがよくあります。例として高さを変更すると、次のように定義されたFrameworkElementで生成されます。

public static readonly DependencyProperty HeightProperty = DependencyProperty.Register( 'Height'、typeof(double)、_ typeofThis、new FrameworkPropertyMetadata((double)1.0 /(double)0.0、 FrameworkPropertyMetadataOptions.AffectsMeasure 、new PropertyChangedCallback(FrameworkElement.OnTransformDirty))、new ValidateValueCallback(FrameworkElement.IsWidthHeightValid))

つまり、高さを変更すると、Measureメソッドがトリガーされ、Measureメソッドは表示されているツリー(この場合は、AdornerDecorator)次に、そのOnChildDesiredSizeChangedメソッドを呼び出し、OnChildDesiredSizeChangedが親コンテナー自体のMeasureメソッドを呼び出します。Measureメソッドは、子コンテナー、装飾コントロール(TextBox)、および 装飾レイヤー(AdornerLayer) もともと同じに属しているAdornerDecoratorだからでAdornerDecorator装飾コントロールは、Measureメソッドで呼び出されます。 装飾層 Measureメソッド、装飾レイヤーは同じ方法でサブクラスを更新します。つまり、SimpleTextBlockAdornerで、子が親を呼び出し、親が子を呼び出し、次に子が親を呼び出しますか?無限ループではないですか?したがって、ここでWPFは変数MeasureInProgressとMeasureDirtyを使用して制御します。既にメジャーにある場合は、ループで呼び出す必要はありません。

レイアウトシステムは非常にリソースを消費すると思いますか? ^ 0 ^

画像

さらに、AdornerのGetDesiredTransformメソッドについては、実際にAdornerLayerのArrangeOverrideレイアウトメソッドを見ました。

protected override Size ArrangeOverride(Size finalSize) { DictionaryEntry[] array = new DictionaryEntry[this._zOrderMap.Count] this._zOrderMap.CopyTo(array, 0) for (int i = 0 i  ArrayList list = (ArrayList)array[i].Value int num2 = 0 while (num2 if (!info.Adorner.IsArrangeValid) { Point location = new Point() info.Adorner.Arrange(new Rect(location, info.Adorner.DesiredSize)) int index = this._children.IndexOf(info.Adorner) if (index >= 0) { Transform transform3 = (proposedTransform != null) ? proposedTransform.AffineTransform : null } } if (info.Adorner.IsClipEnabled) { info.Adorner.AdornerClip = info.Clip } else if (info.Adorner.AdornerClip != null) { info.Adorner.AdornerClip = null } } } return finalSize }

GeneralTransformdesiredTransform = info.Adorner.GetDesiredTransform(info.Transform)

(((アドナー)。この._children [index])。AdornerTransform = transform3

このことから、割り当てはGetDesiredTransformから取得した値をAdornerのAdornerTransformプロパティに返すことであり、AdornerTransformプロパティは実際には

画像

私たちは常にRenderTransformプロパティに精通していますが、精通していませんか?これを見てください。 http://msdn.microsoft.com/zh-cn/library/system.windows.uielement.rendertransform.aspx

RenderTransformを使用すると、レイアウトの後に作成され、視覚的なツリー転送を繰り返す必要がないため、速度は通常のレイアウトよりも速いと言えます。したがって、アニメーションはこの値を変更しようとする必要があります。

私もマークしましたGeneralTransform提案された変換=この.GetProposedTransform(info.Adorner、desiredTransform)好奇心が強い必要があります、どうすればよいですか?実際、これはFlowDirection問題の始まりであり、上記で使用した制御を逆にします。

private GeneralTransform GetProposedTransform(Adorner adorner, GeneralTransform sourceTransform) { if (adorner.FlowDirection == base.FlowDirection) { return sourceTransform } GeneralTransformGroup group = new GeneralTransformGroup() Matrix matrix = new Matrix(-1.0, 0.0, 0.0, 1.0, adorner.RenderSize.Width, 0.0) MatrixTransform transform = new MatrixTransform(matrix) group.Children.Add(transform) if ((sourceTransform != null) && (sourceTransform != Transform.Identity)) { group.Children.Add(sourceTransform) } return group }

第三に、デフォルトのコントロールの適用

画像

GridSplitter グリッドのドラッグコントロールは、誰もが驚くべきではないと思います。移動するためにPreviewAdornerが作成されています。私はこのリンクをウェブ上で見ました。 http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/dfff9b89-81a8-4bfc-852d-d08ccdffe6bb 質問者はWindowのテンプレートを変更し、GridとGridSplitterをテンプレートに配置しました。なぜそれが間違っているのですか?これで、WindowのデフォルトテンプレートにAdornerLayerを生成できるScrollViewerがあり、変更されたテンプレートにはAdornerLayerコンテナがなく、Windowはすでにウィンドウのトップコントロールであることがわかりました。表示されているツリーに沿って他のコントロールはありません。したがって、GridSplitterはAdornerLayerを取得できないため、NullReferenceExceptionがスローされます。解決策は GridSplitter 外側にAdornerLayerのレイヤー、またはScrollViewerまたはAdornerDecoratorがあり、回答者はリンクにAdornerDecoratorを指定します。

画像

検証 検証に使用されたテンプレート、実際には、これらの感嘆符が表示され、外枠はレイヤー上にあり、彼と GridSplitter 違いは、彼が外部でテンプレートを定義できることです。これにより、ユーザーは何を提示するかを指定できます。 TemplatedAdorner、 見つけてみませんか 検証 死んで書かれたためのデフォルトのテンプレート。もちろん、感嘆符を見つけた場合は、外枠が所定の位置にないため、簡単に行うことができます。レイヤーの位置に問題があります。

private static ControlTemplate CreateDefaultErrorTemplate() { ControlTemplate template = new ControlTemplate(typeof(Control)) FrameworkElementFactory factory = new FrameworkElementFactory(typeof(Border), 'Border') factory.SetValue(Border.BorderBrushProperty, Brushes.Red) factory.SetValue(Border.BorderThicknessProperty, new Thickness(1.0)) FrameworkElementFactory child = new FrameworkElementFactory(typeof(AdornedElementPlaceholder), 'Placeholder') factory.AppendChild(child) template.VisualTree = factory template.Seal() return template }

はどうかと言うと AdornedElementPlaceholder このプレースホルダーのサイズは検証コントロール(TextBox)のサイズですが、テンプレートで定義されているため、特定の検証コントロールが何であるかをどのように知ることができますか。 TemplatedAdorner その中のAdornedElementは効果を達成します。奇妙なスキルと言えます AdornedElementPlaceholder 具体的なことを知っている TemplatedAdorner 、できる TemplatedAdorner 具体的にわからない AdornedElementPlaceholder しかし、 AdornedElementPlaceholder また観察された

画像

したがって、彼の効果的な役割は1つだけです。適切な制御は、より適切なデカップリングです。デカップリングの前提は、元のコントロールに特定の予約があることです。 TemplatedAdorner ReferenceElementは、効果を実現するために予約されています。

四。マスクコントロールをカスタマイズする

画像

かゆみが多いと言ったら、簡単にしましょう。データを読み取ってリストが表示されないことを期待する場合、顧客に操作を許可する必要がない場合があるため、このマスクレイヤーを提出する必要があります。顧客が特定のコントロールを操作することを許可しないでください。そうすることで、一方で、顧客は進行状況または操作情報を見ることができます。どのように快適に感じますか?当然、他の場所では影響を受けないので操作できるので、マスクは特定のコントロール専用にしたいです。デモでは簡略化されています。情報を表示するためのテンプレートはカスタマイズ可能で、感覚がなく、ただ言っただけです TemplatedAdorner テンプレートも同様です。だから、テイク主義の精神を再生します。

コードはここにはリストされていませんが、上記のテンプレートコントロールをビジュアルツリーに追加する必要があることに注意してください。そうしないと、通過してブロック効果に到達しません。同様に、操作をトラバースする必要がある場合、ビジュアルツリーに参加することはできません。 。

コードの場合、次のような完全な追加プロパティが好きな人もいます 検証 そのような課題では、私は個人的にクラスの課題を使用することを好みます。気に入らない場合は、自分で変更できます。呼び出しコードは次のとおりです。

<ListView ItemsSource='{Binding Employees}'> <ControlLibrary:MaskAttach.MaskAttach> <ControlLibrary:MaskAttach x:Name='fff' DataContext='{Binding Progress}' Open='{Binding IsLoading}'> <ControlLibrary:MaskAttach.Template> <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition Width='40'/> Grid.ColumnDefinitions> <Rectangle Grid.ColumnSpan='2' Fill='Black' Opacity='0.7'/> <TextBlock Grid.Column='1' Margin='5' Foreground='White' HorizontalAlignment='Right'  VerticalAlignment='Center'> <AccessText Text='{Binding}'/> <AccessText>%AccessText> TextBlock> <ProgressBar Margin='10' Value='{Binding Mode=OneWay}' Height='20'/> Grid> DataTemplate> ControlLibrary:MaskAttach.Template> ControlLibrary:MaskAttach> ControlLibrary:MaskAttach.MaskAttach>

ここで、AdornerLayer.GetAdornerLayerがレイヤーを取得する最後の時間であることにも注意してください。 d.Dispatcher.BeginInvokeに入れます 上記の読み込みを待つ必要がある場合があるため、通常はDispatcherPriorityを選択します レンダリング

DependencyPropertyプロパティ値の変更の検出。Bindingを使用する場合は、オブジェクトをFrameworkElementから派生させる必要がありますが、DependencyPropertyDescriptorを使用して以下を検出できます。

var dpdDataContext = DependencyPropertyDescriptor.FromProperty(MaskAttach.DataContextProperty, maskAttach.GetType()) dpdDataContext.AddValueChanged(maskAttach, delegate { d.SetValue(MaskAttach.DataContextProperty, maskAttach.DataContext) })

五。デコレータ

デコレータがデコレータパターンを考えやすくしているのを見たとき、デコレータは私の印象ではラッパーに近く、元のメソッドをラッパークラスの同じ名前のメソッドに入れ、他のいくつかの関数をのメソッドに追加しました。同名。なくなった。

画像

内部のカラーブロックが関数のサイズを表す場合、パッケージングクラスがより強力であることは明らかです。さらに、彼が関数を追加すると、元のクラスA構造にロスレスになります。ユーザーがインターフェースを介して操作する場合、エンティティークラスを知る必要はありません。

WPFに関する限り、ユーザーが必要としているのはレンダリング効果であり、アウトソーシングレイヤーは表示効果を変更しますが、元のコントロールの効果や機能には影響しません。したがって、彼がコントロールの外観を操作できるBorderコントロール、およびコントロールの実際のサイズを変更するViewBoxはすべて、Decoratorクラス、および前述のAdornerDecoratorから継承されます。どちらも、元のコントロールの外観またはコントロールの拡張です。

画像

デコレータ自体は、単一のコンテナコントロールにすぎません。 1つのコントロールのみを装飾できます。パネルはマルチコンテナです。複数のコントロールを飾ることができます。コントロールの位置の変更も装飾の一形態です。簡単なドラッグアンドドロップコントロールを作成したかったのですが、誰かがオンラインでそれを行っていたことがわかりました。 http://codeblitz.wordpress.com/2009/06/10/wpf-dragdrop-decorator-for-itemscontrol/

ページが壁に囲まれているようです。スナップショットと、後で取得しないように作成したサンプルプログラムが表示されます。

dragdrop_decorator

デフォルトのドラッグアンドドロップ表示と慣行については、以下も記録されます。

1.ドラッグアンドドロップは通常、2つのコントロール間のデータの相互作用です。つまり、ドラッグ(MouseDown)すると、ドラッグ(MouseUp)した後、データを特定の場所に配置し、このデータを別のコントロールに配置するため、2つのコントロールをドラッグアンドドロップします。ドラッグアンドドロップを提供する必要があります。 UIElementから継承するコントロールは、AllowDropプロパティを直接Trueに設定できます。

2.マウスクリックがMouseDownの場合、通常はMouseDownイベントを登録するか、OnMouseDownメソッドを直接オーバーロードし、選択したデータを変数に入れて、コントロールを選択しますか?コントロールはデータの表示であるため、データを取得できるはずです。コントロールに実際にデータがない場合を除いて、それをプルする必要はありません。データがある場合は、そのデータを変数_mouseDownDataに入れます。

3.マウスを動かす過程で、マウスのスタイルが動いていることがわかり(マウスの下に小さな四角があります)、データを渡すメソッドが必要です。 MouseMoveは、マウスが移動すると次のことを行います。文

DragDrop.DoDragDrop((ItemsControl)sender, _mouseDownData, DragDropEffects.Move | DragDropEffects.Copy)

4.マウスを離すと、MouseUpになります。渡されたデータの変数mouseDownDataをクリアするか、Nullに割り当てます。

5. AデータをBにドラッグすると仮定すると、ドラッグには2つの側面があり、Aは呼び出しますドラッグドロップ.DoDragDropメソッドを置く、Bを受け取る方法は? Bコントロールは、PreviewDropイベントを登録し、DragEventArgseパラメーターを介してe.Dataを取得する必要があります。データ型は通常、e.Data.GetDataPresent(typeof(dataof))であるかどうかを判断し、e.Data.GetData(typeof(data)Type))を渡します。具体的に値を取得します。e.Effectsを使用できます。操作を決定するには:Bコントロールにデータを追加するかどうか。

6.将軍に引きずり込まれたときにキャンセルしたい場合はどうなりますか?コントロールは、PreviewQueryContinueDragイベントを登録して、e.ActionをQueryContinueDragEventArgseに割り当てます。 DragAction.Cancelは、DragAction.DropまたはDragAction.Continueにすることもできます。

ここにドラッグに関する別の記事があります: http://www.cnblogs.com/taowen/archive/2008/10/30/1323329.html

6.純粋な個人的な感情

WPFを勉強した最初の月は、その時に何でもできると感じたときに最高の気分だと言えます。ただし、WPFは、以前のWinform開発のアイデアを使用して、OnRenderに光を当て、すべてを実行できると考えることがあります。そうすることで、以前のコントロールが自動的にやり直されます。Microsoftのデフォルトの不快な書き換えを見てください。自分でビルドする人は誰もいません。次に、ゆっくりとMVVMを起動し、多くの変換コントロールを開始します。ただし、デフォルトのコントロールはほとんど最後までです。描画しますが、デフォルトのコントロールを使用して新しいコントロールを詳しく説明することは、チームコミュニケーションの架け橋になります。デフォルトのコントロールは、一般的にニーズを満たすことができます。自分で定義すると、効率に一定の利点がある場合があります。最初も便利で、最後に行います。複雑さも非常に面倒です。最も重要なことは、チームメンバーのスタイルが面倒であり、全体的な効果が影響を与えるということです。以前、コントロールが開発プロセスをスピードアップするという考えを持っていたので、それを使用する必要はありません。実際、私はそれをどのように使うかについてはあまり考えていません。デフォルトのコントロールのモードとアイデアは、勉強して学ぶ価値があります。もちろん、コントロールに特定の効率が必要な場合は、1つしかやり直すことができません。

パターンとオブジェクト指向開発に関しては、責任を明確にし、機能をより詳細にすることがすべてです。責任と機能の意味は何ですか?メンテナンスは簡単ですが、チーム全員の考えです。フォームは同じではありません。あなたはそれがとても良いと思いますが、人々はそれをほとんど理解できません。コミュニケーションが難しいかどうかがわかりにくいです。開発効率はどうですか?ですから、コミュニケーションに役立つデザインは良いデザインです。チームの思考がゆっくりと収束し、才能を伸ばすにつれて、より良い助けやアドバイスを得ることができます。しばらく急ぐ必要がないものもあります。

特に多くの愛好家の助けに感謝します 周永恒 マスターは忙しいことがありますが、答えは少なくて済みますが、分析するのを手伝ってください。そして 周永恒 しかし、それはあなたが分析を詳細に説明するのを助け、そしてここであなたの心からの感謝を表すことができます。

MaskControl DragAndDropDecorator

示してください

転載:https://www.cnblogs.com/Curry/archive/2009/09/16/WPFDecorator.html