0

WPF での作業 ダブルクリックすると、編集可能な TextBox に「変換」される TextBlock を生成しようとしています。その後、Enter キー、Esc キー、またはフォーカスを失うと、編集が終了し、TextBox が TextBlock に戻ります。

私が見つけた解決策はほとんど機能しています。私の問題は、「変換」に TextBox をフォーカスできないため、ユーザーが要素をもう一度明示的にクリックしてフォーカスし、編集を開始する必要があることです。

コードの説明

この動作を実装するために選択した方法は、要素のテンプレートを変更するために、テンプレートとスタイルの DataTriggers を操作することです。私が示す例では、要素は単純な ContentControl ですが、これを実行しようとしている実際の使用例は少し複雑です (私は ListBox を持っており、この動作を通じて各要素を一度に 1 つずつ編集できます)。 )。

アイデアは次のとおりです。

  • ContentControl には関連付けられた Style があります
  • 関連する Style は、ContentTemplate を TextBlock として定義します。
  • モデル オブジェクトにはプロパティ InEditing があり、コントロールを編集するときに true になります。
  • TextBlock の MouseBinding を介して、モデルの InEditing プロパティを True に設定します。
  • スタイルには、InEditing をリッスンする DataTrigger があり、この場合、ContentTemplate を TextBox に設定します。
  • EventSetters を通じて、Enter、Esc、および LostFocus をキャッチして、変更を保存し、以前のスタイルに戻します。よく注意してください: イベントを TextBox に直接添付することはできません。代わりに EventSetter を使用してください。

最適ではありませんが (特に InEditing プロパティでは、ビューとモデルの動作の特定の組み合わせがあり、KeyDown と LostFocus のさまざまなハンドラーを介して TextBox のモデルへの変更のコミット ロジックを大幅に再実装するのは好きではありません)。 )、システムは実際に問題なく動作します。

失敗した実装アイデア

最初は、TextBox の IsVisibleChanged イベントに接続して、そこにフォーカスを設定することを考えました。前述の理由により、実行できませんStyle 内の Target タグにイベントを指定することはできません。代わりに EventSetter を使用してください。

このようなイベントはルーティング イベントではなく、EventSetter では使用できないため、エラーによって提案された解決策は使用できません。

コード

コードは 4 つのファイルに分割されています。

Model.cs:

using System.Windows;

namespace LeFocus
{
    public class Model: DependencyObject
    {
        public bool InEditing
        {
            get { return (bool)GetValue(InEditingProperty); }
            set { SetValue(InEditingProperty, value); }
        }

        // Using a DependencyProperty as the backing store for InEditing.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty InEditingProperty =
            DependencyProperty.Register("InEditing", typeof(bool), typeof(Model), new UIPropertyMetadata(false));


        public string Name
        {
            get { return (string)GetValue(NameProperty); }
            set { SetValue(NameProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Name.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty NameProperty =
            DependencyProperty.Register("Name", typeof(string), typeof(Model), new UIPropertyMetadata("Hello!"));
    }
}

アプリ.xaml:

<Application x:Class="LeFocus.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:lefocus="clr-namespace:LeFocus"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <lefocus:Model x:Key="Model"/>
    </Application.Resources>
</Application>

MainWindow.xaml:

<Window x:Class="LeFocus.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:lefocus="clr-namespace:LeFocus"
        Title="MainWindow" Height="350" Width="525"
        DataContext="{Binding Source={StaticResource Model}}" 
        Name="mainWindow">
    <Window.Resources>
        <Style x:Key="SwitchingStyle"
               TargetType="{x:Type ContentControl}">
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate DataType="{x:Type lefocus:Model}">
                        <TextBlock Text="{Binding Path=Name}">
                            <TextBlock.InputBindings>
                                <MouseBinding MouseAction="LeftDoubleClick"
                                              Command="lefocus:MainWindow.EditName"
                                              CommandParameter="{Binding}"/>
                            </TextBlock.InputBindings>
                        </TextBlock>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
            <EventSetter Event="TextBox.KeyDown" Handler="TextBox_KeyDown"/>
            <EventSetter Event="TextBox.LostFocus" Handler="TextBox_LostFocus"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=InEditing}" Value="True">
                    <Setter Property="ContentTemplate">
                        <Setter.Value>
                            <DataTemplate DataType="{x:Type lefocus:Model}">
                                <TextBox Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=lefocus:MainWindow, AncestorLevel=1}, Path=NameInEditing, UpdateSourceTrigger=PropertyChanged}" TextChanged="TextBox_TextChanged" KeyDown="TextBox_KeyDown_1" />
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Window.CommandBindings>
        <CommandBinding Command="lefocus:MainWindow.EditName" Executed="setInEditing"/>
    </Window.CommandBindings>
    <Grid>
        <ContentControl Style="{StaticResource SwitchingStyle}" Content="{Binding}"/>
    </Grid>
</Window>

MainWindow.xaml.cs

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace LeFocus
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }


        public string NameInEditing
        {
            get { return (string)GetValue(NameInEditingProperty); }
            set { SetValue(NameInEditingProperty, value); }
        }

        // Using a DependencyProperty as the backing store for NameInEditing.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty NameInEditingProperty =
            DependencyProperty.Register("NameInEditing", typeof(string), typeof(MainWindow), new UIPropertyMetadata(null));


        public static readonly RoutedUICommand EditName =
            new RoutedUICommand("EditName", "EditName", typeof(MainWindow));


        private void setInEditing(object sender, ExecutedRoutedEventArgs e)
        {
            var model = ((Model)e.Parameter);
            NameInEditing = model.Name;
            model.InEditing = true;
        }


        private void TextBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                var model = getModelFromSender(sender);
                model.Name = NameInEditing;
                NameInEditing = null;
                model.InEditing = false;
            }
            else if (e.Key == Key.Escape)
            {
                var model = getModelFromSender(sender);
                model.InEditing = false;
            }
        }

        private void TextBox_LostFocus(object sender, RoutedEventArgs e)
        {
            var model = getModelFromSender(sender);
            model.Name = NameInEditing;
            NameInEditing = null;
            model.InEditing = false;
        }

        private static Model getModelFromSender(object sender)
        {
            var contentControl = (ContentControl)sender;
            var model = (Model)contentControl.DataContext;
            return model;
        }
    }
}
4

2 に答える 2

0

この設定で機能する可能性のある1つの方法は、を処理LoadedしてTextBoxからそれを処理するKeyboard.Focusことです(sender)。

于 2012-03-10T15:32:01.630 に答える
0

WPF でフォーカスが失われたイベントでリストボックス テンプレートを変更するのコードは、既に必要なことを行っていると思います。これは、動作がより明確になるように、リストボックスの選択四角形を非表示にする変更です。リストボックス項目が選択されると (シングル クリックで)、テキスト ボックスの境界線が表示されますが、マウスがその上にある場合のみであり、まだ編集可能ではありません (入力してみてください)。もう一度クリックするか、最初にダブルクリックしてアイテムを選択すると、編集できます。

<Page.Resources>
    <ResourceDictionary>

        <Style x:Key="NullSelectionStyle" TargetType="ListBoxItem">
            <Style.Resources>
                <!-- SelectedItem with focus -->
                <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" />
                <!-- SelectedItem without focus -->
                <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Transparent" />
                <!-- SelectedItem text foreground -->
                <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="{DynamicResource {x:Static SystemColors.ControlTextColorKey}}" />
            </Style.Resources>
            <Setter Property="FocusVisualStyle" Value="{x:Null}" />
        </Style>

        <Style x:Key="ListBoxSelectableTextBox" TargetType="{x:Type TextBox}">
            <Setter Property="IsHitTestVisible" Value="False" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}, AncestorLevel=1}}" Value="True">
                    <Setter Property="IsHitTestVisible" Value="True" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ResourceDictionary>
</Page.Resources>
<Grid>
    <ListBox ItemsSource="{Binding Departments}" HorizontalContentAlignment="Stretch">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBox Margin="5" Style="{StaticResource ListBoxSelectableTextBox}" Text="{Binding Name}" BorderBrush="{x:Null}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>
于 2012-03-11T19:41:50.660 に答える