none
CListCtrlの中にCButtonやCComboBoxを配置したい RRS feed

  • 質問

  • 表題のことをやりたいのですが良い方法がありましたらご教授お願いします。

    VisualCは2010のMFCを使っています。 (今回のご質問は VC++MFC限定になります)

    ボタンは各行の1カラム目に4つボタンを配置、 他の1つのカラムにコンボボックスを配置する予定です。

    現在試したところ、

    CListCtrlの子としてCButtonを作成すれば表示ができるようですが、今のところCButtonを20個作成しても初めの1つしか表示しません。

    また縦方向にボタンを配置しても起動時は見えませんが、スクロールすれば見えて、CListCtrlの行を選択するとボタンが消えたりします。

    判らないのは、起動時に表示できている1つのボタンだけは、選択しても消えません。

    この辺りの含めて上手い配置方法がありましたら 教えてください。



    • 編集済み AppKey 2016年4月29日 1:44
    2016年4月28日 14:48

回答

  • CWinFormsXxx クラスは、いずれも「MFC」のクラスですが、それでもダメなんですかね?

    アプリケーション全体は基本的に今まで通りです(/clrスイッチだけ追加するだけでコード変更は一切不要)。CListCtrl を差し替えるだけの最小限の変更にとどめたいのなら、CWinFormsControl に置き換えて、System::Windows::Forms::DataGrid クラスを指定してやればいいと思います(詳しくはリファレンス参照)。

    実行環境のOSなどがわからないので、どの程度の制約があるかわかりませんが、今のOS(Vista以上)にとって、.NET Framework はシステムコンポーネントです。昔でいえば、ComCtl32.dll 6.0と同じような存在と言えます。

    もし、そういう状況であったとしても .NET Framework というシステムコントロール類は使えないということであれば、下手に食い下がって意固地になられるよりは、CListCtrl でカスタムドローを使ったグリッドコントロールをつくるという形になると思います。

    こちらは、子ウィンドウでボタンを貼るとスクロール問題、フォーカス問題という解決の難しい問題のほかに、システムリソース枯渇という、OSから作り直さないと解決できない問題にも直面するのでお勧めしません。

    コンボボックスの編集動作については、エディット同様、LVN_BEGINLABELEDITメッセージをトリガーにして、処理すればいいと思います。エディットも表示するコントロールが違うだけでやることは一緒です。

    ちなみに、フォーカスを持っていないときの描画だけならコンボボックスも、DrawThemeBackground でできます(中身の描画はほかのテーマ描画APIで行う)。

    それと、オーナードローと、カスタムドローは異なります。カスタムドローは、本当に標準とは違う描画にしたいところだけ、描画します。

    例えば、特定のセルだけ自分で書く、アイコン描画をImageList指定ではなく自前で描画する、条件によってボールドで描画するなどなど。。。

    描画範囲の制御や表示最適化の大半は、CListCtrl自身が賄ってくれるので、呼ばれたところだけ書いてればいいというメリットもありますし、本当に書くべきところだけしか通知が来ないのでスクロールエンドとかそういうことも考慮しなくて済みます。


    とっちゃん@わんくま同盟, Visual Studio and Development Technologies http://blogs.wankuma.com/tocchann/default.aspx

    • 回答としてマーク AppKey 2016年4月30日 23:16
    2016年4月29日 4:42

すべての返信

  • 表題のことをやりたいのですが良い方法がありましたらご教授お願いします。

    説明不足でした。

    CListCtrlの各行の中に、複数のCButtonや CComboBoxを配置したいと考えています。

    みなさんよろしくお願い致します。

    2016年4月28日 15:33
  • 開発環境のバージョンは何でしょうか? ボタンを押したときの処理や、コンボボックスの中身というのはどんな内容なのでしょうか? リストに入れるデータ件数(行数)は最大でどの程度ですか? 質問するときは、ぼんやりとしたあいまいな話に終始するのではなく、前提条件や具体的に実現したい内容をもっと詳しく書きましょう。

    なお、C++およびMFCで凝ったUIを作ろうとすると膨大な手間がかかります。もしどうしてもMFCに固執しなければならない理由が特になく、かつC#/VB.NETを利用してもよいのであれば、WPFやWinRTで開発することを強く推奨します。

    とりあえずWPFを試してみる気があるのであれば、まずVisual Studio 2015で[Visual C#]の[WPF アプリケーション]プロジェクトに「WpfListViewTest1」という名前を付けて作成して、下記のコードをそれぞれ入力してビルド&実行してみてください。

    MainWindow.xaml.cs:

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Linq;
    using System.Runtime.CompilerServices;
    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 WpfListViewTest1
    {
      /// <summary>
      /// MainWindow.xaml の相互作用ロジック
      /// </summary>
      public partial class MainWindow : Window
      {
        private ObservableCollection<Magic> _myMagics = new ObservableCollection<Magic>();
    
        public MainWindow()
        {
          InitializeComponent();
    
          this._myMagics.Add(new Magic() { Name = "フレイム", AttackAttribute = MagicalAttribute.Fire });
          this._myMagics.Add(new Magic() { Name = "サンダー", AttackAttribute = MagicalAttribute.Wind });
          this._myMagics.Add(new Magic() { Name = "コールド", AttackAttribute = MagicalAttribute.Water });
          this._myMagics.Add(new Magic() { Name = "クエイク", AttackAttribute = MagicalAttribute.Earth });
          this._myMagics.Add(new Magic() { Name = "ほげ", AttackAttribute = MagicalAttribute.None });
    
          this.listView1.ItemsSource = this._myMagics;
        }
      }
    
      public enum MagicalAttribute
      {
        None,
        Fire,
        Wind,
        Water,
        Earth,
      }
    
      public class MinimumDelegeteCommand : ICommand
      {
        public MinimumDelegeteCommand() {}
    
        public event Action<object> ExecuteAction;
    
        public event EventHandler CanExecuteChanged;
    
        public void RaiseCanExecuteChanged()
        {
          this.CanExecuteChanged?.Invoke(this, EventArgs.Empty);
        }
    
        public bool CanExecute(object parameter)
        {
          return true;
        }
    
        public void Execute(object parameter)
        {
          this.ExecuteAction?.Invoke(parameter);
        }
      }
    
      public class Magic : INotifyPropertyChanged
      {
        private string _name;
        private MagicalAttribute _attackAttribute = MagicalAttribute.None;
    
        public Magic()
        {
          var cmd = new MinimumDelegeteCommand();
          cmd.ExecuteAction += (s) => { this.PlayVoice(); };
          this.VoiceTestPlayCommand = cmd;
        }
    
        public string Name
        {
          get { return this._name; }
          set
          {
            if (this._name != value)
            {
              this._name = value;
              this.NotifyPropertyChangedByCallerInfo();
            }
          }
        }
    
        public MagicalAttribute AttackAttribute
        {
          get { return this._attackAttribute; }
          set
          {
            if (this._attackAttribute != value)
            {
              this._attackAttribute = value;
              this.NotifyPropertyChangedByCallerInfo();
            }
          }
        }
    
        public ICommand VoiceTestPlayCommand { get; private set; }
    
        public void PlayVoice()
        {
          MessageBox.Show($"{this.Name}!! (属性: {this.AttackAttribute})", "なんちゃってボイス再生");
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected void NotifyPropertyChangedByCallerInfo([CallerMemberName] string propertyName = "")
        {
          this.NotifyPropertyChanged(propertyName);
        }
    
        protected void NotifyPropertyChanged(string propertyName)
        {
          this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
      }
    }
    

    MainWindow.xaml:

    <Window
        x:Class="WpfListViewTest1.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:WpfListViewTest1"
        mc:Ignorable="d"
        Title="MainWindow"
        WindowStartupLocation="CenterScreen"
        Width="525"
        Height="350"
        >
        <Window.Resources>
            <ResourceDictionary>
                <ObjectDataProvider x:Key="MagicalAttributeKey" MethodName="GetValues" ObjectType="{x:Type local:MagicalAttribute}">
                    <ObjectDataProvider.MethodParameters>
                        <x:Type TypeName="local:MagicalAttribute"/>
                    </ObjectDataProvider.MethodParameters>
                </ObjectDataProvider>
            </ResourceDictionary>
        </Window.Resources>
    
        <Grid>
            <ListView Name="listView1">
                <ListView.ItemContainerStyle>
                    <Style TargetType="ListViewItem">
                        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                    </Style>
                </ListView.ItemContainerStyle>
                <ListView.View>
                    <GridView>
                        <GridViewColumn Header="名前" Width="100">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBox Text="{Binding Path=Name}"/>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        <GridViewColumn Header="詠唱ボイス" Width="100">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <Button Content="再生テスト" Command="{Binding VoiceTestPlayCommand}"/>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        <GridViewColumn Header="攻撃属性" Width="100">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <ComboBox IsReadOnly="True" ItemsSource="{Binding Source={StaticResource MagicalAttributeKey}}" SelectedItem="{Binding Path=AttackAttribute}"/>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                    </GridView>
                </ListView.View>
            </ListView>
        </Grid>
    </Window>
    
    XAMLの記述は慣れが必要ですが、柔軟性や拡張性・カスタマイズ性はMFCやWindows Formsの比ではありません。WPF/WinRTとMVVMデザインパターンを活用することで、ビューとモデルをきれいに分離することもできます。
    2016年4月28日 18:33
  • 開発環境のバージョンは何でしょうか? ボタンを押したときの処理や、コンボボックスの中身というのはどんな内容なのでしょうか? リストに入れるデータ件数(行数)は最大でどの程度ですか? 質問するときは、ぼんやりとしたあいまいな話に終始するのではなく、前提条件や具体的に実現したい内容をもっと詳しく書きましょう。

    なお、C++およびMFCで凝ったUIを作ろうとすると膨大な手間がかかります。もしどうしてもMFCに固執しなければならない理由が特になく、かつC#/VB.NETを利用してもよいのであれば、WPFやWinRTで開発することを強く推奨します。

    とりあえずWPFを試してみる気があるのであれば、まずVisual Studio 2015で[Visual C#]の[WPF アプリケーション]プロジェクトに「WpfListViewTest1」という名前を付けて作成して、下記のコードをそれぞれ入力してビルド&実行してみてください。

    MainWindow.xaml.cs:

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Linq;
    using System.Runtime.CompilerServices;
    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 WpfListViewTest1
    {
      /// <summary>
      /// MainWindow.xaml の相互作用ロジック
      /// </summary>
      public partial class MainWindow : Window
      {
        private ObservableCollection<Magic> _myMagics = new ObservableCollection<Magic>();
    
        public MainWindow()
        {
          InitializeComponent();
    
          this._myMagics.Add(new Magic() { Name = "フレイム", AttackAttribute = MagicalAttribute.Fire });
          this._myMagics.Add(new Magic() { Name = "サンダー", AttackAttribute = MagicalAttribute.Wind });
          this._myMagics.Add(new Magic() { Name = "コールド", AttackAttribute = MagicalAttribute.Water });
          this._myMagics.Add(new Magic() { Name = "クエイク", AttackAttribute = MagicalAttribute.Earth });
          this._myMagics.Add(new Magic() { Name = "ほげ", AttackAttribute = MagicalAttribute.None });
    
          this.listView1.ItemsSource = this._myMagics;
        }
      }
    
      public enum MagicalAttribute
      {
        None,
        Fire,
        Wind,
        Water,
        Earth,
      }
    
      public class MinimumDelegeteCommand : ICommand
      {
        public MinimumDelegeteCommand() {}
    
        public event Action<object> ExecuteAction;
    
        public event EventHandler CanExecuteChanged;
    
        public void RaiseCanExecuteChanged()
        {
          this.CanExecuteChanged?.Invoke(this, EventArgs.Empty);
        }
    
        public bool CanExecute(object parameter)
        {
          return true;
        }
    
        public void Execute(object parameter)
        {
          this.ExecuteAction?.Invoke(parameter);
        }
      }
    
      public class Magic : INotifyPropertyChanged
      {
        private string _name;
        private MagicalAttribute _attackAttribute = MagicalAttribute.None;
    
        public Magic()
        {
          var cmd = new MinimumDelegeteCommand();
          cmd.ExecuteAction += (s) => { this.PlayVoice(); };
          this.VoiceTestPlayCommand = cmd;
        }
    
        public string Name
        {
          get { return this._name; }
          set
          {
            if (this._name != value)
            {
              this._name = value;
              this.NotifyPropertyChangedByCallerInfo();
            }
          }
        }
    
        public MagicalAttribute AttackAttribute
        {
          get { return this._attackAttribute; }
          set
          {
            if (this._attackAttribute != value)
            {
              this._attackAttribute = value;
              this.NotifyPropertyChangedByCallerInfo();
            }
          }
        }
    
        public ICommand VoiceTestPlayCommand { get; private set; }
    
        public void PlayVoice()
        {
          MessageBox.Show($"{this.Name}!! (属性: {this.AttackAttribute})", "なんちゃってボイス再生");
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected void NotifyPropertyChangedByCallerInfo([CallerMemberName] string propertyName = "")
        {
          this.NotifyPropertyChanged(propertyName);
        }
    
        protected void NotifyPropertyChanged(string propertyName)
        {
          this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
      }
    }

    MainWindow.xaml:

    <Window
        x:Class="WpfListViewTest1.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:WpfListViewTest1"
        mc:Ignorable="d"
        Title="MainWindow"
        WindowStartupLocation="CenterScreen"
        Width="525"
        Height="350"
        >
        <Window.Resources>
            <ResourceDictionary>
                <ObjectDataProvider x:Key="MagicalAttributeKey" MethodName="GetValues" ObjectType="{x:Type local:MagicalAttribute}">
                    <ObjectDataProvider.MethodParameters>
                        <x:Type TypeName="local:MagicalAttribute"/>
                    </ObjectDataProvider.MethodParameters>
                </ObjectDataProvider>
            </ResourceDictionary>
        </Window.Resources>
    
        <Grid>
            <ListView Name="listView1">
                <ListView.ItemContainerStyle>
                    <Style TargetType="ListViewItem">
                        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                    </Style>
                </ListView.ItemContainerStyle>
                <ListView.View>
                    <GridView>
                        <GridViewColumn Header="名前" Width="100">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBox Text="{Binding Path=Name}"/>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        <GridViewColumn Header="詠唱ボイス" Width="100">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <Button Content="再生テスト" Command="{Binding VoiceTestPlayCommand}"/>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        <GridViewColumn Header="攻撃属性" Width="100">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <ComboBox IsReadOnly="True" ItemsSource="{Binding Source={StaticResource MagicalAttributeKey}}" SelectedItem="{Binding Path=AttackAttribute}"/>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                    </GridView>
                </ListView.View>
            </ListView>
        </Grid>
    </Window>
    XAMLの記述は慣れが必要ですが、柔軟性や拡張性・カスタマイズ性はMFCやWindows Formsの比ではありません。WPF/WinRTとMVVMデザインパターンを活用することで、ビューとモデルをきれいに分離することもできます。

    お答えとサンプルのソース大変ありがとうございます

    本案件はVC++MFC限定となってます。

    2016年4月28日 23:14
  • "MFC ListBox Button" でぐぐると、下記のサンプル紹介のある stackoverflow のスレッドに当たりました。
    http://stackoverflow.com/questions/6991645/how-to-add-a-button-in-a-listcontrol-mfc

    試してはいませんが、参考になりそうなコンテンツです。

    追伸
    古い MFC アプリで部分的に C++/CLI を介して C# の Form や Window を呼ぶといったこともできるので、画面の作成工数を抑えたいという場合は検討するのも手かと。
    MFC の画面を1から書ける人はだいぶ少なくなってきているはずなので。

    2016年4月29日 0:11
    モデレータ
  • Resありがとうございました。

    直ぐ検索できるところはチェックしてました。

    情報をいただいたサイトはノーコメントで必要のないコードが大量にあったため細かく調査してない状況です。

    要件としてはMFC限定になってます。

    2016年4月29日 1:40
  • 基本的に MFC は大きな拡張がされていない状態ですし、凝った画面を作るには泥臭いコードを書くことになります。
    (MFC は Windows のコントロールに対してラップしただけの簡素なものに、Feature Pack といった外部の買い物を足したぐらいの認識)

    サンプルや実績をお求めなのかもしれませんが、そういった泥臭いコードはできればやりたくないと思うのがプログラマーなので、価値を損なわない程度の仕様改善で逃げていることが多いのではないでしょうか?
    どうしても強く、現状の仕様が求められているのであれば、「コメントがないから調査しない」ではなく、デバッグしながらでも動きを知る、自分が作り出したいものにつなげられるノウハウを拾うしかないと思います…、大変ですが。

    (ListBox の子に Button を置くというのは単なるウィンドウの親子関係であり、リストではありません。リストの考え方を維持したいのであれば、自分で ListBox の中身を描画するか、ListBox に見せかけてスクロール可能・選択可能な子コントロール(パネル、ダイアログとも称する)を作り出すしかありません。
    ただ、今の試行錯誤の様子を見ると、失礼ながら、実現までに月単位でかかるのではないかと心配になりますが…)

    2016年4月29日 2:26
    モデレータ
  • 基本的に MFC は大きな拡張がされていない状態ですし、凝った画面を作るには泥臭いコードを書くことになります。
    (MFC は Windows のコントロールに対してラップしただけの簡素なものに、Feature Pack といった外部の買い物を足したぐらいの認識)

    サンプルや実績をお求めなのかもしれませんが、そういった泥臭いコードはできればやりたくないと思うのがプログラマーなので、価値を損なわない程度の仕様改善で逃げていることが多いのではないでしょうか?
    どうしても強く、現状の仕様が求められているのであれば、「コメントがないから調査しない」ではなく、デバッグしながらでも動きを知る、自分が作り出したいものにつなげられるノウハウを拾うしかないと思います…、大変ですが。

    (ListBox の子に Button を置くというのは単なるウィンドウの親子関係であり、リストではありません。リストの考え方を維持したいのであれば、自分で ListBox の中身を描画するか、ListBox に見せかけてスクロール可能・選択可能な子コントロール(パネル、ダイアログとも称する)を作り出すしかありません。
    ただ、今の試行錯誤の様子を見ると、失礼ながら、実現までに月単位でかかるのではないかと心配になりますが…)

    お答えありがとうございます。

    クライアントの意向によりかわりますが

    現状ではボタンやコンボボックスは画像にして自分で描画するか、 選択されている行だけを対象としてコントロール類はCListCtrlの外に出せるか 検討しています。

    2016年4月29日 3:18
  • いわゆるグリッド的なものを作りたいということですかね?

    まずは、実装コストが低いところで、CWinFormsControl, CWinFormsDialog, CWinFormsView + .NET Framework(System.WIndows.Forms系)のグリッドコントロール。

    どこに貼るかで変わるので、詳しくはリファレンスを見てもらうとして、Windows Updateでブロックしていない限り、Vista以上のすべてのOSに.NET4はインストールされています。

    それでも、.NET Frameworkは使えないという場合。

    CListCtrl のカスタムドローを使って、ボタンのようなものを描画し、マウスやキーボード処理を駆使して、コントロールのようにふるまわせることになります。

    ボタンを貼ると、スクロールさせたときについてこない(あとから追従ならできますけど画面崩れまくりです)とか、フォーカスを持っていかれるとおかしくなるとか。。。
    CListCtrl自身が、子ウィンドウを用意できるような仕組みになっていないので、とてつもなく面倒です。

    カスタムドローについては、NM_CUSTOMDRAW をキーワードに調べてみてください。

    ボタン描画は、DrawThemeBackgroundあたりを足掛かりにすればいいと思います。

    コントロールを貼ってやり過ごそうとする方が大変だと思います(経験的に)。


    とっちゃん@わんくま同盟, Visual Studio and Development Technologies http://blogs.wankuma.com/tocchann/default.aspx

    2016年4月29日 3:35
  • いわゆるグリッド的なものを作りたいということですかね?

    まずは、実装コストが低いところで、CWinFormsControl, CWinFormsDialog, CWinFormsView + .NET Framework(System.WIndows.Forms系)のグリッドコントロール。

    どこに貼るかで変わるので、詳しくはリファレンスを見てもらうとして、Windows Updateでブロックしていない限り、Vista以上のすべてのOSに.NET4はインストールされています。

    それでも、.NET Frameworkは使えないという場合。

    CListCtrl のカスタムドローを使って、ボタンのようなものを描画し、マウスやキーボード処理を駆使して、コントロールのようにふるまわせることになります。

    ボタンを貼ると、スクロールさせたときについてこない(あとから追従ならできますけど画面崩れまくりです)とか、フォーカスを持っていかれるとおかしくなるとか。。。
    CListCtrl自身が、子ウィンドウを用意できるような仕組みになっていないので、とてつもなく面倒です。

    カスタムドローについては、NM_CUSTOMDRAW をキーワードに調べてみてください。

    ボタン描画は、DrawThemeBackgroundあたりを足掛かりにすればいいと思います。

    コントロールを貼ってやり過ごそうとする方が大変だと思います(経験的に)。


    とっちゃん@わんくま同盟, Visual Studio and Development Technologies http://blogs.wankuma.com/tocchann/default.aspx

    情報大変ありがとうございます。

    クライアントからの依頼で VC++MFCと言われていますのでこれ以外は使えない状況です。

    今のところコントロール類はCListCtrlの外に出すか、

    ご提案のようにOwnerDrawでそれっぽいのをCDCで描画しようと思っています。 ただコンボは動作まで作るのは面倒ですので、クリックしたらその場の同サイズをコンボを配置して、フォーカスが外れたら消す ような事を考えています。

    2016年4月29日 3:59
  • CWinFormsXxx クラスは、いずれも「MFC」のクラスですが、それでもダメなんですかね?

    アプリケーション全体は基本的に今まで通りです(/clrスイッチだけ追加するだけでコード変更は一切不要)。CListCtrl を差し替えるだけの最小限の変更にとどめたいのなら、CWinFormsControl に置き換えて、System::Windows::Forms::DataGrid クラスを指定してやればいいと思います(詳しくはリファレンス参照)。

    実行環境のOSなどがわからないので、どの程度の制約があるかわかりませんが、今のOS(Vista以上)にとって、.NET Framework はシステムコンポーネントです。昔でいえば、ComCtl32.dll 6.0と同じような存在と言えます。

    もし、そういう状況であったとしても .NET Framework というシステムコントロール類は使えないということであれば、下手に食い下がって意固地になられるよりは、CListCtrl でカスタムドローを使ったグリッドコントロールをつくるという形になると思います。

    こちらは、子ウィンドウでボタンを貼るとスクロール問題、フォーカス問題という解決の難しい問題のほかに、システムリソース枯渇という、OSから作り直さないと解決できない問題にも直面するのでお勧めしません。

    コンボボックスの編集動作については、エディット同様、LVN_BEGINLABELEDITメッセージをトリガーにして、処理すればいいと思います。エディットも表示するコントロールが違うだけでやることは一緒です。

    ちなみに、フォーカスを持っていないときの描画だけならコンボボックスも、DrawThemeBackground でできます(中身の描画はほかのテーマ描画APIで行う)。

    それと、オーナードローと、カスタムドローは異なります。カスタムドローは、本当に標準とは違う描画にしたいところだけ、描画します。

    例えば、特定のセルだけ自分で書く、アイコン描画をImageList指定ではなく自前で描画する、条件によってボールドで描画するなどなど。。。

    描画範囲の制御や表示最適化の大半は、CListCtrl自身が賄ってくれるので、呼ばれたところだけ書いてればいいというメリットもありますし、本当に書くべきところだけしか通知が来ないのでスクロールエンドとかそういうことも考慮しなくて済みます。


    とっちゃん@わんくま同盟, Visual Studio and Development Technologies http://blogs.wankuma.com/tocchann/default.aspx

    • 回答としてマーク AppKey 2016年4月30日 23:16
    2016年4月29日 4:42
  • 貴重な情報ありがとうございました。
    2016年4月29日 10:15
  • リストはアイコンでなく「表」の方だと仮定します。

    MFC限定ですと、HWNDを持つオブジェクトをCListCtrl上に多数配置するのは、
    CListCtrlの仕様上の性質から、非常に困難です。
    まったく不可能と言うわけではありませんが、コスト的に現実的ではないか
    又は実用的でない速度でしか動作しないと予測できます。

    特にCListCtrlのスクロール量と配置した子コントロールの位置の制御が難しいです。
    従って、HWNDを持つボタンを配置するより、
    「ボタンと同等に動作をする様にカラムの表示を制御する」
    という実装の方がはるかに簡単です。

    一方、コンボボックスはドロップダウンリストタイプになるかと思いますが、
    この場合、同時にドロップダウンするのは一つだけですので、通常動作時は選択内容(文字列等)を表示しており、
    ユーザーがクリックした場合、その位置に、ドロップダウンして表示すれば十分だと考えられます。
    このコンボボックスは不要な時は非表示にしておけば良いわけですね。

    参考になれば幸いです。

    2016年4月30日 1:38
  • お答え大変ありがとうございました。

    今回はクライアントとの相談によって、コントロール類の配置はCListCtrlの外に出すことになりました。

    今回の解答から得た知識は貴重なものですので今後の糧とさせて頂きます。

    みなさん、大変ありがとうございました。

    2016年4月30日 23:16