4

私は C# Net 2.0 Windows Forms に、アプリケーションの起動時にデータで満たされた約 10 列の ListView を持っています。データは巨大であるため、途中で高速にリロードすることはできません。My ListView には詳細ビューがあり、ユーザーは各列のサイズを個別に変更できます。

ユーザーは、一度に 10 個の列のいずれかまたは複数の列を非表示にし、非特定の行でいつでも列を再表示できるものとします。列を非表示にしている間、データは削除されません。

次のことを試しましたが、結果は満足のいくものではありませんでした:

1) サイズ 0 で列のサイズを変更すると、ユーザーが列をいじり始めるまで列が多少見えなくなります。私の ListView には、ユーザーが各列のサイズを手動で変更できるオプションがあるため、ユーザーは誤ってサイズを変更してしまいます。

2) 列を削除するだけで、次の問題が発生します。列を再度追加しようとすると、列はその位置とサイズを覚えていません。位置が主な問題です。その理由を説明しようと思います。ユーザーが最初に「列 2」を非表示にしてから「列 3」を非表示にし、後で 2 の前に 3 を非表示にすると、「インデックス 2」が存在しないため、インデックス 3 に挿入できず、例外が発生します。削除する前にインデックスの位置を覚えていても、以前の列が既に非表示になっているかどうか、またはどの列が前後に欠落しているか、または非表示になっているかがわからないため、単に列をそのインデックスに戻すことはできません。したがって、シナリオは次のようになります。1 を表示、2 を非表示、3 も非表示、4 を表示、5 を非表示、6 を非表示、7 を非表示、8 を表示、9 を非表示、10 を非表示。

可能な解決策 "1)" と "2)" は、シナリオで自動的に除外されます。列の幅をゼロにすることも良いですが、ユーザーは必要に応じていつでも列のサイズを変更できるため、サイズをゼロに変更すると、ユーザーから非表示にすることはできません。ユーザーはサイズ変更によってそれを再表示し、私のシステムはそれがまだ隠されていると見なします.

誰かがより良いアイデアを持っていますか? listView 列に「visible」または「hide」プロパティがないのはなぜですか? 誰かが以前にそれを行った場合は、解決策を投稿してください。

データが追加されているときに、リストビュー内のすべての列の自動サイズ変更を使用することを追加する必要があります。このため、以下の答えは機能しません。サイズ変更イベントは、-1 の幅を検出できません。幅 0 の「すべての非表示列」は、データが追加されたときにサイズ変更されます。リストビューは列の長さを超えるデータを切り取るので、永久に自動サイズ変更する必要があります。Explorer では列がデータの長さに収まるため、この問題は発生しません。C# には高度なリストビューがありません。ここでは、データが追加されるたびに列を -1 に設定する必要があります。この概念では、列を非表示にするための column.width = 0 のアイデアは機能しません。

4

2 に答える 2

8

OK、あなたの問題は実際にはListView列を非表示にする方法ですか? . これは、ウェブ上で多くの人から尋ねられました。色々と探してみましたが見つかりませんでした。私は唯一の解決策になりました:列幅をゼロに設定します。これは、ここでいくつかのトリックで機能します。

//This will try hiding the column at index 1
listView1.Columns[1].Width = 0;
//ColumnWidthChanging event handler of your ListView
private void listView1_ColumnWidthChanging(object sender, ColumnWidthChangingEventArgs e){
  if(e.ColumnIndex == 1){
     e.Cancel = true;
     e.NewWidth = 0;
  }
}

ほぼ完璧に機能します。ただし、ユーザーがpipe非表示の列の位置にマウスを移動すると、 There is a Zero-width column here, just hold mouse down and drag to resize のCursorようなユーザーに通知するインジケーターが表示されます。もちろん、ユーザーはゼロからサイズを変更することはできません。これは、上記のコードのように作成するためです。しかし、そのような操作を通知すると、少し厄介になります。問題を示すスクリーンショットを次に示します。CancelNewWidth = 0Cursor

ここに画像の説明を入力

この問題を解決するのは簡単ではありません。少なくとも私はそう感じています。うまくいくと思われるこの解決策を考えました。アイデアは、マウスが非表示の列のパイプの近くにあるかどうかを検出する必要があり、設定する必要があるということCursor = Cursors.Arrowです。これがあなたにぴったりだと思うクラス全体です:

public class CustomListView : ListView
{
    [DllImport("user32")]
    private static extern bool EnumChildWindows(IntPtr parentHwnd, EnumChildProc proc, object lParam);
    delegate bool EnumChildProc(IntPtr childHwnd, object lParam);
    public CustomListView()
    {
        VisibleChanged += (s, e) =>
        {
            if (Visible && headerHandle == IntPtr.Zero&&!DesignMode)
            {
                EnumChildWindows(Handle, EnumChild, null);
                headerProc = new HeaderProc(this);
                headerProc.AssignHandle(headerHandle);
            }
        };
        columnPipeLefts[0] = 0;
    }      
    //Save the Handle to the Column Headers, a ListView has only child Window which is used to render Column headers  
    IntPtr headerHandle;
    //This is used use to hook into the message loop of the Column Headers
    HeaderProc headerProc;        
    private bool EnumChild(IntPtr childHwnd, object lParam)
    {
        headerHandle = childHwnd;
        return true;
    }
    //Updated code
    protected override void WndProc(ref Message m)
    {
        if (m.Msg == 0x101e&&hiddenColumnIndices.Contains(m.WParam.ToInt32()))//WM_SETCOLUMNWIDTH = 0x101e
        {                
            if(m.LParam.ToInt32() > 0) hiddenColumnWidths[m.WParam.ToInt32()] = m.LParam.ToInt32();                    
            return;//Discard the message changing hidden column width so that it won't be shown again.                
        }
        base.WndProc(ref m);
    }
    //Save the column indices which are hidden
    List<int> hiddenColumnIndices = new List<int>();
    //Save the width of hidden columns
    Dictionary<int, int> hiddenColumnWidths = new Dictionary<int, int>();
    //Save the Left (X-Position) of the Pipes which separate Column Headers.
    Dictionary<int, int> columnPipeLefts = new Dictionary<int, int>();
    protected override void OnColumnWidthChanging(ColumnWidthChangingEventArgs e)
    {
        if (hiddenColumnIndices.Contains(e.ColumnIndex))
        {
            e.Cancel = true;
            e.NewWidth = 0;
        }
        base.OnColumnWidthChanging(e);
    }
    //We need to update columnPipeLefts whenever the width of any column changes
    protected override void OnColumnWidthChanged(ColumnWidthChangedEventArgs e)
    {            
        base.OnColumnWidthChanged(e);
        UpdateColumnPipeLefts(Columns[e.ColumnIndex].DisplayIndex + 1);
    }
    int index = -1;
    protected override void OnColumnReordered(ColumnReorderedEventArgs e)
    {
        int i = Math.Min(e.NewDisplayIndex, e.OldDisplayIndex);
        index = index != -1 ? Math.Min(i + 1, index) : i + 1;            
        base.OnColumnReordered(e);                                
    }
    //This is used to update the columnPipeLefts every reordering columns or resizing columns.
    private void UpdateColumnPipeLefts(int fromIndex)
    {
        int w = fromIndex > 0 ? columnPipeLefts[fromIndex - 1] : 0;
        for (int i = fromIndex; i < Columns.Count; i++)
        {
            w += i > 0 ? Columns.OfType<ColumnHeader>().Where(k=>k.DisplayIndex == i - 1).Single().Width : 0;
            columnPipeLefts[i] = w;
        }
    }
    //This is used to hide a column with ColumnHeader passed in
    public void HideColumn(ColumnHeader col)
    {
        if (!hiddenColumnIndices.Contains(col.Index))
        {                
            hiddenColumnWidths[col.Index] = col.Width;//Save the current width to restore later                
            col.Width = 0;//Hide the column
            hiddenColumnIndices.Add(col.Index);
        }
    }
    //This is used to hide a column with column index passed in
    public void HideColumn(int columnIndex)
    {
        if (columnIndex < 0 || columnIndex >= Columns.Count) return;
        if (!hiddenColumnIndices.Contains(columnIndex))
        {
            hiddenColumnWidths[columnIndex] = Columns[columnIndex].Width;//Save the current width to restore later                
            Columns[columnIndex].Width = 0;//Hide the column
            hiddenColumnIndices.Add(columnIndex);
        }
    }
    //This is used to show a column with ColumnHeader passed in
    public void ShowColumn(ColumnHeader col)
    {
        hiddenColumnIndices.Remove(col.Index);
        if(hiddenColumnWidths.ContainsKey(col.Index))
           col.Width = hiddenColumnWidths[col.Index];//Restore the Width to show the column
        hiddenColumnWidths.Remove(col.Index);
    }
    //This is used to show a column with column index passed in
    public void ShowColumn(int columnIndex)
    {
        if (columnIndex < 0 || columnIndex >= Columns.Count) return;
        hiddenColumnIndices.Remove(columnIndex);
        if(hiddenColumnWidths.ContainsKey(columnIndex))
           Columns[columnIndex].Width = hiddenColumnWidths[columnIndex];//Restore the Width to show the column            
        hiddenColumnWidths.Remove(columnIndex);
    }
    //The helper class allows us to hook into the message loop of the Column Headers
    private class HeaderProc : NativeWindow
    {
        [DllImport("user32")]
        private static extern int SetCursor(IntPtr hCursor);
        public HeaderProc(CustomListView listView)
        {
            this.listView = listView;
        }
        bool mouseDown;
        CustomListView listView;
        protected override void WndProc(ref Message m)
        {                
            if (m.Msg == 0x200 && listView!=null && !mouseDown)
            {
                int x = (m.LParam.ToInt32() << 16) >> 16;
                if (IsSpottedOnAnyHiddenColumnPipe(x)) return;
            }
            if (m.Msg == 0x201) { 
                mouseDown = true;
                int x = (m.LParam.ToInt32() << 16) >> 16;
                IsSpottedOnAnyHiddenColumnPipe(x);
            }
            if (m.Msg == 0x202) mouseDown = false;
            if (m.Msg == 0xf && listView.index != -1 && MouseButtons == MouseButtons.None) { //WM_PAINT = 0xf
                listView.UpdateColumnPipeLefts(listView.index); 
                listView.index = -1; 
            };
            base.WndProc(ref m);
        }
        private bool IsSpottedOnAnyHiddenColumnPipe(int x)
        {                
            foreach (int i in listView.hiddenColumnIndices.Select(j=>listView.Columns[j].DisplayIndex))
            {
                if (x > listView.columnPipeLefts[i] - 1 && x < listView.columnPipeLefts[i] + 15)
                {
                    SetCursor(Cursors.Arrow.Handle);
                    return true;
                }
            }
            return false;
        }
    }
}
于 2013-08-08T01:21:52.497 に答える
0

ここでコードの最初の行をこれに置き換えてください - 内部リスト ビュー API をよりエレガントに使用してヘッダー ハンドルを取得します -古いコードは @EnumChildWindows をクラッシュさせます):

        [DllImport("user32.dll", EntryPoint = "SendMessage")]
    private static extern IntPtr SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);
    const int LVM_FIRST = 0x1000;
    const int LVM_GETHEADER = (LVM_FIRST + 31);

    public CustomListView()
    {
        VisibleChanged += (s, e) =>
        {
            if (Visible && headerHandle == IntPtr.Zero && !DesignMode)
            {
                IntPtr hwnd = SendMessage(this.Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);
                if (hwnd != null)
                {
                    headerProc = new HeaderProc(this);
                    headerHandle = hwnd;
                    headerProc.AssignHandle(hwnd);
                }
            }
        };

        columnPipeLefts[0] = 0;
    }
于 2016-05-12T12:51:20.390 に答える