質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.05%

WPF コンテキストメニューを開いた場所のTextBlockを編集できるようにしたい

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,949

score 13

前提・実現したいこと

(無知なため、誤った用語の使い方をしていたら申し訳ありません。)

現在、練習も兼ねて自分用で某ゲームの簡単な補助ツールを制作しています。
エンドユーザ側で編集を行いたいTextBlockを沢山配置するため、コンテキストメニューから、何らかの方法でクリックした部分にあるTextBlockを編集できるようにする方法を教えていただきたいです。
また、可能であれば時刻の記録や計算も行いたいため、そういった物も同様に扱える方法が望ましいです。
「多少操作方法が変わるが、容易に実装できる」といった方法でも構いません。
よろしくお願いします。

該当のソースコード

MainWindow.xaml

<Window x:Class="Kuro.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Kuro"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>

        <TextBlock HorizontalAlignment="Left" Margin="120,10,0,0" TextWrapping="Wrap" Text="Sun" VerticalAlignment="Top"/>

        <Grid HorizontalAlignment="Left" Height="18" Margin="80,30,0,0" VerticalAlignment="Top" Width="100">

            <Border BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Left" Height="18" VerticalAlignment="Top" Width="100">
                <Border.ContextMenu>
                    <ContextMenu>
                        <MenuItem Header="キャラ名編集" Click="CName_Edit"/>
                    </ContextMenu>
                </Border.ContextMenu>

                <TextBlock TextWrapping="Wrap" x:Name="Chara1" Text="キャラ1"  HorizontalAlignment="Center"/>



            </Border>
        </Grid>

        <TextBlock HorizontalAlignment="Left" Margin="228,10,0,0" TextWrapping="Wrap" Text="Mon" VerticalAlignment="Top"/>

        <Grid HorizontalAlignment="Left" Height="18" Margin="190,30,0,0" VerticalAlignment="Top" Width="100">

            <Border BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Left" Height="18" VerticalAlignment="Top" Width="100">
                <Border.ContextMenu>
                    <ContextMenu>
                        <MenuItem Header="キャラ名編集" Click="CName_Edit"/>
                    </ContextMenu>
                </Border.ContextMenu>

                <TextBlock TextWrapping="Wrap" x:Name="Chara2" Text="キャラ2"  HorizontalAlignment="Center"/>



            </Border>
        </Grid>
        (中略)
    </Grid>
</Window>


MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Kuro
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

        }

        /// <summary>
        /// キャラ名編集
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void CName_Edit(object sender, RoutedEventArgs e)
        {

            //キャラ名入力
            string CName = Microsoft.VisualBasic.Interaction.InputBox("", "キャラ名入力", Chara1.Text, -1, -1);
            Chara1.Text = CName; //ここでChara2以降にも代入したい
        }
    }
}

試したこと

  • foreach(var child in LogicalTreeHelper.GetChildren(this))でTextBlockを取得し、InputBoxに入力した物を代入しようとした
  • コンテキストメニューの親Gridを取得し、その子コントロールとしてTextBlockを生成しようとした(知識不足で断念)

補足情報(FW/ツールのバージョンなど)

Microsoft Visual Studio Community 2017 version 15.9.5
Microsoft .NET Framework version 4.7.03056

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • Gurz1019_MP

    2019/01/14 22:43

    常時入力可能では問題がありますか?

    キャンセル

  • kiyure

    2019/01/14 22:53

    同じ要領で時刻の記録と計算を行おうと思っているので、できればそれは避けたいです。
    (質問文から完全に抜けていました、すみません)

    キャンセル

  • Gurz1019_MP

    2019/01/15 22:51

    TextBoxを避けたい理由をもう少し詳しく教えてください。現状の要求ですとTextBoxで対応可能なように思います。

    キャンセル

  • kiyure

    2019/01/16 08:26

    本文を編集した時点で大きな勘違いをしていて、それを直さずそのままにしていたようです。
    度々申し訳ありません。

    キャンセル

回答 2

checkベストアンサー

+2

キャラ名を表示しているTextBlockをTextBoxに変更し、コンテキストメニューを押した時にこれのIsReadOnlyを変更することで実現可能だと思います。またXamlを見た所、同じパターンのUIが並ぶような構造になっていますが、こういったUIはItemsControl系のコントロールを使うことで一定のテンプレートを繰り返し適用できます。
サンプルコードを載せます。MVVMに関してはこちらの質問も参考にしてください。MVVMでModelを使いたい。

<Window x:Class="WpfApp3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp3"
        xmlns:vm="clr-namespace:WpfApp3.ViewModel"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Window.DataContext>
        <vm:MainWindowViewModel/>
    </Window.DataContext>

    <Grid>
        <ListBox ItemsSource="{Binding Characters}" VerticalAlignment="Top">
            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
            <ListBox.ItemTemplate>
                <DataTemplate DataType="{x:Type vm:Character}">
                    <Grid Width="100">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>
                        <TextBlock Text="{Binding Header}" HorizontalAlignment="Center"/>
                        <Grid Grid.Row="1">
                            <Border BorderBrush="Black" BorderThickness="1" Height="18">
                                <Border.ContextMenu>
                                    <ContextMenu>
                                        <MenuItem Header="キャラ名編集" Command="{Binding StartEditNameCommand}"/>
                                    </ContextMenu>
                                </Border.ContextMenu>
                                <TextBox Text="{Binding Name}"
                                         HorizontalContentAlignment="Center"
                                         IsReadOnly ="{Binding IsNameReadOnly}">
                                    <i:Interaction.Triggers>
                                        <i:EventTrigger EventName="LostFocus">
                                            <i:InvokeCommandAction Command="{Binding EndEditNameCommand}"/>
                                        </i:EventTrigger>
                                    </i:Interaction.Triggers>
                                </TextBox>
                            </Border>
                        </Grid>
                    </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Window>
using System.Collections.ObjectModel;
using WpfApp3.Common;

namespace WpfApp3.ViewModel
{
    class MainWindowViewModel : NotificationObject
    {
        public MainWindowViewModel()
        {
            Characters = new ObservableCollection<Character>()
            {
                new Character() { Header = "Sun", Name = "キャラ1" },
                new Character() { Header = "Mon", Name = "キャラ2" }
            };
        }

        private ObservableCollection<Character> _characters;

        public ObservableCollection<Character> Characters
        {
            get { return _characters; }
            set
            {
                if (_characters != value)
                {
                    _characters = value;
                    RaisePropertyChanged(nameof(Characters));
                }
            }
        }
    }
}
using WpfApp3.Common;

namespace WpfApp3.ViewModel
{
    public class Character : NotificationObject
    {
        #region Header変更通知プロパティ

        private string _header;

        public string Header
        {
            get { return _header; }
            set
            {
                if (_header != value)
                {
                    _header = value;
                    RaisePropertyChanged(nameof(Header));
                }
            }
        }

        #endregion

        #region Name変更通知プロパティ

        private string _Name;

        public string Name
        {
            get { return _Name; }
            set
            {
                if (_Name != value)
                {
                    _Name = value;
                    RaisePropertyChanged(Name);
                }
            }
        }

        #endregion

        #region IsNameReadOnly変更通知プロパティ

        private bool _isNameReadOnly = true;

        public bool IsNameReadOnly
        {
            get { return _isNameReadOnly; }
            set
            {
                if (_isNameReadOnly != value)
                {
                    _isNameReadOnly = value;
                    RaisePropertyChanged(nameof(IsNameReadOnly));
                }
            }
        }

        #endregion

        #region StartEditNameCommandプロパティ

        private DelegateCommand _startEditNameCommand;

        public DelegateCommand StartEditNameCommand
        {
            get
            {
                if (_startEditNameCommand == null)
                {
                    _startEditNameCommand = new DelegateCommand(StartEditName);
                }
                return _startEditNameCommand;
            }
        }

        #endregion

        #region EndEditNameCommandプロパティ

        private DelegateCommand _endEditNameCommand;

        public DelegateCommand EndEditNameCommand
        {
            get
            {
                if (_endEditNameCommand == null)
                {
                    _endEditNameCommand = new DelegateCommand(EndEditName);
                }
                return _endEditNameCommand;
            }
        }

        #endregion

        public void StartEditName()
        {
            IsNameReadOnly = false;
        }

        public void EndEditName()
        {
            IsNameReadOnly = true;
        }
    }
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/15 00:20

    長くなりすぎたので追記です。サンプルの実行にはSystem.Windows.Interactivityへの参照設定が必要です。このサンプルでは編集中にテキストボックスがフォーカスを失うと、IsReadOnlyがTrueに戻るようになっています。ItemsControlとしてListBoxを使用しています。MainWindowViewModelのCharactersを増やすと、UIが同じパターンで右に伸びていきます。

    キャンセル

  • 2019/01/16 08:31 編集

    解説と貼っていただいたサンプルコードを参考に、実装ができました。
    非常に分かりやすい回答ありがとうございました。

    キャンセル

+1

単純に、はじめから全部TextBoxでいいのでは。

IsReadOnly IsEnabled を使うと入力の制御が行える。
https://code.msdn.microsoft.com/windowsdesktop/XAML-WPF-201ae943

デフォルトの表示が気に入らないなら、Styleで表示を変えればいいと思う。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/14 23:06

    すみません、本文から抜けていた事として、時刻の記録や計算も行いたいのです。

    キャンセル

  • 2019/01/15 03:42

    ↑くわしく。

    キャンセル

  • 2019/01/16 08:47

    「その時点での時刻を記録し、1週間後の時刻を表示する」という物で、現在Gurz1019_MPさんの回答を元に制作しています。
    今回は本筋から外れるため、あまり言及はしませんでした。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.05%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る