2

私は現在、ユーザーが動的にコンパイルおよび実行される C# スクリプトを入力するプロジェクトに取り組んでいます。後でプロジェクトの他の側面について質問する可能性があるため、詳細についてはここでは省略します。

私には制御できない高レベルの決定がいくつかあるため、ユーザー インターフェイスは WPF を使用して開発され、Quantum Whale Editor.NET コントロールがエディターとして使用されます。悲しいことに、QWhale Editor.NET の WPF バージョンはまだ完全に成熟していないようです。そのため、ドキュメントが不足しており、最悪の場合、バインディング フレンドリーではないようです。

私はまだ WPF に慣れていませんが、MVVM にはある程度慣れているので、それを適用したいと思っています。ただし、評価版をテストしていて、エディターのテキストをモデルのプロパティにバインドしようとしたときに最初の課題に直面し、それが不可能であるという例外を受け取りました。

タイプ 'TextEditor' の 'Text' プロパティに 'Binding' を設定することはできません。「Binding」は、DependencyObject の DependencyProperty でのみ設定できます。

代替案を確認しながら AvalonEdit を試してみたところ、別のスタック オーバーフローの質問であるMaking AvalonEdit MVVM Compatibleを思い出しました。そして、私は同じコンセプトに従いました。

エディターから継承するクラスを定義し、依存関係プロパティを追加して、最初Textnew. しかし、明らかにそれはロング ショットであり、私のプロパティは使用されませんでした。基本プロパティが直接呼び出されていました。

それが失敗したら、 という新しいプロパティを定義しましたDocumentText。私はそれを wrapbase.Textにし、それを使用してバインディングを定義し、それに応じて一方向のバインディングが機能するようにしました。それはモデルからコントロールまでです。しかし、私が見つけたものから、バインディングを他の方向に動作させる最良の方法はoverrideOnTextChangedイベント (または同等のもの) にプロパティ変更通知を発生させることです。問題は、奇妙に聞こえるかもしれませんが、コントロールにそのようなイベントがないことです。

これで、テキストを変更する可能性のあるすべてのアクション (入力、ドラッグ & ドロップ、貼り付けなど) を処理できるように、おそらく他のイベント (OnKeyUp、OnMouseClick など) の束をオーバーライドできますが、それもあまりないようです。実用的であり、後でバインドすることに関心がある可能性のある他のプロパティには再現できない可能性があります。そして、時間を見つけて数日間オンラインで検索した後でも、他のアイデアを見つけるにはまだ近づいていません. それで、コントロール自体のコードに飛び込む以外に、私の問題に対する適切な解決策はありますか? (おそらく、ライセンスによってソースコードにアクセスできるようになりますが、それを直接変更することは避けたいと思います)。

探しているものがこの特定のコントロールに依存しないと感じたため、質問のタイトルとタグで QWhale エディターを指定することは避けました。間違っている場合は修正してください。

別のコンピューターを使用しているため、現時点ではテスト コードを提供できませんが、必要であると思われる場合は、メモを残してください。追加します。


更新: これがコードです。問題を明確に説明できたかどうか確信が持てないためです。

class ExtenEdit : TextEditor
{
    public static DependencyProperty TextProperty =
        DependencyProperty.Register("Text", typeof(string), typeof(ExtenEdit),
            new PropertyMetadata((obj, args) =>
            {
                TextEditor target = (TextEditor)obj;
                target.DocumentText = (string)args.NewValue;
            })
        );

    public new string Text
    {
        get { return base.Text; }
        set
        {
            if (base.Text != value)
            {
                base.Text = value;
            }
        }
    }
}

の値を変更すると機能しViewModelますが、エディターに入力すると、プロパティがバイパスされ、基本プロパティが直接呼び出されます。次のようなものを追加すると機能します。

protected override void OnKeyUp(System.Windows.Input.KeyEventArgs e)
{
    SetCurrentValue(TextProperty, base.Text);
    base.OnKeyUp(e);
}

しかし、上で述べたように、それを有効な「クリーンな」ソリューションとは考えられません。

そして、これが私の XAML の関連部分です (私が AvalonDock でも遊んでいることがわかります)。

名前空間:

<Window x:Class="AvalonDockQWhale.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:avalonDock="http://avalondock.codeplex.com"
    xmlns:converter="clr-namespace:AvalonDockQWhale.Converter"
    xmlns:pane="clr-namespace:AvalonDockQWhale.View.Pane"
    xmlns:editor="clr-namespace:QWhale.Editor.Wpf;assembly=QWhale.Editor.Wpf"
    xmlns:control="clr-namespace:AvalonDockQWhale.Control"
    xmlns:controlHelper="clr-namespace:AvalonDockQWhale.ControlHelper"
    x:Name="mainWindow"
    Title="MainWindow" Height="600" Width="800">

そして、私の最初の試みのバインディング:

<control:ExtenEdit Text="{Binding Path=ScriptText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

DHN のソリューションで試したバインディング:

<editor:TextEditor controlHelper:AttachedProperties.Text="{Binding ScriptText}" />

<editor:TextEditor controlHelper:AttachedProperties.Text="{Binding Path=ScriptText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

最終的な詳細: DHN からの回答を受け入れましたが、私の特定のケースでは機能しませんが、同様の問題に対する適切な解決策と思われるためです。

4

1 に答える 1

1

これは、別のSOlerを支援するために昨日書いたものです。しかし、それはあなたのニーズにも合うかもしれません。Windowviaを「拡張」してDependencyProperty、そのDialogResultプロパティをバインド可能にしました。

始める

public class AttachedProperties : DependencyObject //adds a bindable DialogResult to window
{
    public static readonly DependencyProperty DialogResultProperty = 
        DependencyProperty.RegisterAttached("DialogResult", typeof(bool?), typeof(AttachedProperties), 
        new PropertyMetaData(default(bool?), OnDialogResultChanged));

    public bool? DialogResult
    {
        get { return (bool?)GetValue(DialogResultProperty); }
        set { SetValue(DialogResultProperty, value); }
    }

    private static void OnDialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var window = d as Window;
        if (window == null)
            return;

        window.DialogResult = (bool?)e.NewValue;
    }
}

DialogResult を VM にバインドし、プロパティの値を設定できるようになりました。値が設定されると、ウィンドウが閉じます。

<!-- Assuming that the VM is bound to the DataContext and the bound VM has a property DialogResult -->
<Window someNs:AttachedProperties.DialogResult={Binding DialogResult} />

終わり

IMHO 特定の 1 つのプロパティに対してのみ必要な場合は、バインド可能性を提供する良い方法です。そのように改善する必要があるプロパティがさらにある場合は、クラスを拡張またはラップします。

編集 - これは、本番環境で実行されているものの要約です

<Window x:Class="AC.Frontend.Controls.DialogControl.Dialog"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:DialogControl="clr-namespace:AC.Frontend.Controls.DialogControl" 
        xmlns:hlp="clr-namespace:AC.Frontend.Helper"
        MinHeight="150" MinWidth="300" ResizeMode="NoResize" SizeToContent="WidthAndHeight"
        WindowStartupLocation="CenterScreen" Title="{Binding Title}"
        hlp:AttachedProperties.DialogResult="{Binding DialogResult}" WindowStyle="ToolWindow" ShowInTaskbar="True"
        Language="{Binding UiCulture, Source={StaticResource Strings}}">
        <!-- A lot more stuff here -->
</Window>

ご覧のとおり、xmlns:hlp="clr-namespace:AC.Frontend.Helper"最初に名前空間を宣言し、その後で binding を宣言していますhlp:AttachedProperties.DialogResult="{Binding DialogResult}"

AttachedProperty見た目はこんな感じ。以前に投稿したものとは異なりますが、違いはありません。

public class AttachedProperties
{
    #region DialogResult

    public static readonly DependencyProperty DialogResultProperty =
        DependencyProperty.RegisterAttached("DialogResult", typeof (bool?), typeof (AttachedProperties), new PropertyMetadata(default(bool?), OnDialogResultChanged));

    private static void OnDialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var wnd = d as Window;
        if (wnd == null)
            return;

        wnd.DialogResult = (bool?) e.NewValue;
    }

    public static bool? GetDialogResult(DependencyObject dp)
    {
        if (dp == null) throw new ArgumentNullException("dp");

        return (bool?)dp.GetValue(DialogResultProperty);
    }

    public static void SetDialogResult(DependencyObject dp, object value)
    {
        if (dp == null) throw new ArgumentNullException("dp");

        dp.SetValue(DialogResultProperty, value);
    }

    #endregion
}
于 2013-04-24T06:43:53.083 に答える