简体   繁体   English

在属性网格中选择一个属性

[英]Select a property in the property grid

I am using a PropertyGrid to display the content of an object to the user. 我正在使用PropertyGrid向用户显示对象的内容。

This PropertyGrid is synchronized with an Excel sheet, assuming a cell value match a property value. 假定单元格值与属性值匹配,则此PropertyGrid与Excel工作表同步。

As the user selects a property in the PropertyGrid , the application highlights the corresponding cell in the Excel sheet which is opened beside. 当用户在PropertyGrid选择PropertyGrid ,应用程序将突出显示旁边打开的Excel工作表中的相应单元格。 I can do this using the SelectedGridItemChanged event. 我可以使用SelectedGridItemChanged事件来做到这一点。

Now, I want to have a property selected in my PropertyGrid when the user selects a cell in the Excel sheet. 现在,当用户在Excel工作表中选择一个单元格时,我想在PropertyGrid中选择一个属性

void myWorkbook_SheetSelectionChangeEvent(NetOffice.COMObject Sh, Excel.Range Target)
{
    if (eventMask > 0)
        return;

    try
    {
        eventMask++;

        this.Invoke(new Action(() => 
        {
            propertyGrid1.SelectedGridItem = ... // ?
        }
    }
    finally
    {
        eventMask--;
    }
}

I noticed that the SelectedGridItem can be written to. 我注意到SelectedGridItem可以写入。

Unfortunately I do not find a way to access the GridItems collection of my PropertyGrid so I can look for the right GridItem and select it. 不幸的是,我找不到访问我的PropertyGridGridItems集合的方法,因此我可以寻找合适的GridItem并将其选中。

How can I do this? 我怎样才能做到这一点?

You can get all the GridItems from the root. 您可以从根目录获取所有GridItems。 I am using the below code to retrieve all the griditems within a property grid 我正在使用下面的代码来检索属性网格内的所有网格项

private GridItem Root
    {
        get
        {
            GridItem aRoot = myPropertyGrid.SelectedGridItem;
            do
            {
                aRoot = aRoot.Parent ?? aRoot;
            } while (aRoot.Parent != null);
            return aRoot;
        }
    }

and pass the root to the below method 并将根传递给以下方法

private IList<GridItem> GetAllChildGridItems(GridItem theParent)
    {
        List<GridItem> aGridItems = new List<GridItem>();
        foreach (GridItem aItem in theParent.GridItems)
        {
            aGridItems.Add(aItem);
            if (aItem.GridItems.Count > 0)
            {
                aGridItems.AddRange(GetAllChildGridItems(aItem));
            }
        }
        return aGridItems;
    }

I came up against this quite a bit a project so I wrote an extension method for it: 我碰到了一个相当多的项目,因此为它编写了一个扩展方法:

if (!SetupManagerSettings.BootStrapperLocation.IsFile()) // Just another extension method to check if its a file
{
    settingsToolStripMenuItem.Checked = true;  // Event handler OnChecked ensures the settings panel is unhidden
    settingsPropertyGrid.ActivateControl();

    settingsPropertyGrid.SelectPropertyGridItemByName("BootStrapperLocation"); // Here is the extension method 

    return false;
}

Here is the extension method with a private, supporting method for traversing the hierarchy of objects (if this applies to your object model): 这是带有私有支持方法的扩展方法,用于遍历对象的层次结构(如果这适用于您的对象模型):

    public static bool SelectPropertyGridItemByName(this PropertyGrid propertyGrid, string propertyName)
    {
        MethodInfo getPropEntriesMethod = propertyGrid.GetType().GetMethod("GetPropEntries", BindingFlags.NonPublic | BindingFlags.Instance);

        Debug.Assert(getPropEntriesMethod != null, @"GetPropEntries by reflection is still valid in .NET 4.6.1 ");

        GridItemCollection gridItemCollection = (GridItemCollection)getPropEntriesMethod.Invoke(propertyGrid, null);

        GridItem gridItem = TraverseGridItems(gridItemCollection, propertyName);

        if (gridItem == null)
        {
            return false;
        }

        propertyGrid.SelectedGridItem = gridItem;

        return true;
    }

    private static GridItem TraverseGridItems(IEnumerable parentGridItemCollection, string propertyName)
    {
        foreach (GridItem gridItem in parentGridItemCollection)
        {
            if (gridItem.Label != null && gridItem.Label.Equals(propertyName, StringComparison.OrdinalIgnoreCase))
            {                   
                return gridItem;
            }

            if (gridItem.GridItems == null)
            {
                continue;
            }

            GridItem childGridItem = TraverseGridItems(gridItem.GridItems, propertyName);

            if (childGridItem != null)
            {
                return childGridItem;
            }
        }

        return null;
    } 

I looked at the above options and did not like them that much, I altered it a bit found that this works well for me 我查看了上述选项,但并不太喜欢它们,我对其进行了一些更改,发现这对我来说很有效

bool TryFindGridItem(PropertyGrid grid, string propertyName, out GridItem discover)
{
    if (grid is null)
    {
        throw new ArgumentNullException(nameof(grid));
    }

    if (string.IsNullOrEmpty(propertyName))
    {
        throw new ArgumentException("You need to provide a property name", nameof(propertyName));
    }

    discover = null;
    var root = pgTrainResult.SelectedGridItem;
    while (root.Parent != null)
        root = root.Parent;

    foreach (GridItem item in root.GridItems)
    {
        //let's not find the category labels 
        if (item.GridItemType!=GridItemType.Category)
        {
            if (match(item, propertyName))
            {
                discover= item;
                return true;

            }
        }
        //loop over sub items in case the property is a group

        foreach (GridItem child in item.GridItems)
        {
            if (match(child, propertyName))
            {
                discover= child;
                return true;
            }
        }


        //match based on the property name or the DisplayName if set by the user
        static bool match(GridItem item, string name)
            => item.PropertyDescriptor.Name.Equals(name, StringComparison.Ordinal) || item.Label.Equals(name, StringComparison.Ordinal);


    }
    return false;
}

it uses a local method named match, if you're version of C# does not allow it then just put it external to the method and perhaps give it a better name. 它使用一个名为match的本地方法,如果您使用的C#版本不允许使用它,则只需将其放在该方法的外部即可,也许可以给它起一个更好的名称。

Match looks at the DisplayName as well as the property name and returns true if either is a match, this might not be what you would like so then update that. Match会查看DisplayName以及属性名称,如果其中一个匹配,则返回true,这可能不是您想要的,因此请对其进行更新。

In my usecase I need to select the property based on a value the user can select so call the above method like this: 在我的用例中,我需要根据用户可以选择的值来选择属性,因此调用上述方法如下:

if (TryFindGridItem(pgMain, propertyName, out GridItem gridItem))
{                
     gridItem.Select();
}

It could theoretically never not find it unless the user selects a string that is not proper this way the if can be updated using an else .. I rather keep it safe then have a null-pointer exception if I can't find the name specified. 从理论上讲,除非用户选择了不合适的字符串,否则无法找到它,否则可以使用else来更新if ..我宁愿保持它的安全,如果找不到指定的名称,则会出现null指针异常。

Also I am not going into sub classes and lists of classes as my use case doesn't need that, if yours does than perhaps make recursive calls in the GridItem. 另外,我不打算进入子类和类列表,因为用例不需要它,如果您的用例不需要,则可以在GridItem中进行递归调用。

Small note, replace GridItem with var and the code brakes as at compile time the IDE has no clue what a GridItemCollection returns… 请注意,用var替换GridItem并代码制动,因为在编译时IDE并不知道GridItemCollection返回什么…

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM