65.9K
CodeProject 正在变化。 阅读更多。
Home

集成属性视图

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.44/5 (6投票s)

2004年2月29日

CPOL

9分钟阅读

viewsIcon

58255

downloadIcon

1752

应用程序集成属性视图

Sample Image - PropertiesView.jpg

引言

Visual Studio 7 中的属性窗口成为触发事件和设置向导之前已完成的状态的新方法。我认为最好能在我的代码中拥有这个机制。我已经使用了 MS SwitchSplit 示例来测试此视图。它可能不如使用干净的项目那样清晰,但 SwitchSplit 具有很多功能。它还能阻止我在现有应用程序中添加属性视图时做出假设。到目前为止,我有四个属性连接。一个连接到死胡同集,一个连接到应用程序树视图、列表视图和 form2 视图。

CPropertiesView

这只是一个 CView 派生类,只有一个公开的成员和一个应用程序级别的事件处理程序,它们都执行相同的操作。该视图可以创建,也可以不创建 CPropertiesDoc。一旦连接了 CpropertiesViewSet,视图将自行维护。您无需对其进行任何特殊操作。列表控件具有一个分割器功能,可以保持分割比例不变。光标设置在可编辑字段中单击。键盘访问功能基本正常。
void CPropertiesView::ConnectSet( 
  CPropertiesViewSet& pl, bool activate= true )
CpropertiesViewSet 连接到属性视图。如果您不想将您连接的 CpropertiesViewSet 成为活动集,则 activate 应为 false。可以重复调用此函数而不会造成损害。

另一种选择是使用

AfxGetMainWnd( )->SendMessageToDescendants( 
  ID_REG_PVM_CONNECT, (WPARAM)&thePropSet );
这是一个已注册的消息,因此您不必担心冲突。通过消息传递,您不必拥有属性视图的指针。LParam 是可选的。因为它默认为 NULL,所以如果您不希望激活属性集而只进行连接,则将其设置为 true。

CPropertiesViewSet

这是您将要使用的类。您为希望在属性视图中公开的每个应用程序属性集创建一个 CpropertiesViewSet 对象,并将其连接到视图。连接机制是自包含的。如果视图在属性集销毁之前被销毁,您也不会弄乱。如果您在视图中销毁了一个集,视图将只会被清除。目前 CpropertiesViewSet 不派生自 CObject,因此代码中没有 kindof 的断言。

构造函数

CpropertiesViewSet( long nId= 0, long nGrp= 0 );

您可以在构造时或稍后设置 ID 和组号。ID 在连接的您这边使用,以便您可以在事件回调中区分 CpropertiesViewSet 对象。

组号用于当您有多个 CpropertiesViewSet 对象希望它们出现在视图的下拉标题中时。如果设置为零,则该集将与其他所有集分离。这是在属性视图中在各个集之间切换的工具栏的次优选择。树视图和列表视图在示例中共享下拉标题。

事件连接

我最终使用了回调来进行事件连接。上面描述的连接仅将 CpropertiesViewSet 与视图连接起来。但是,要使对象有用,拥有 CpropertiesViewSet 的对象需要在视图中发生更改时得到通知。这就是事件连接。此连接不是必需的,有时您可能不需要它。但是,例如,如果您使用下面描述的 evUser 或 evUserNE 类型,则没有此连接它们将无法正常工作。没有此连接,您可以通过 ReloadProperties() 更新视图,并且视图中的任何更改都会立即在 CpropertiesViewSet 中可用。要进行事件连接,请使用 SetEventCallback。您将需要一个具有原型的全局或静态成员

bool CALLBACK function( LPVOID owner, int action, int item, int id )
  • owner 是当前连接的所有者对象。
  • action 按下按钮,编辑更改等。
  • item 是属性项列表中的索引。
  • id 连接的 CpropertiesViewSet 的 ID,可选使用。
  • return 如果更改被接受,则返回 True。您也可以在返回之前更改该项,并且该更改将反映在视图中。

此连接只需要连接一次,但可以重复连接而不会造成损害。您可以让一个静态函数独立共享属性视图的同一种对象。您的父对象(所有者)的生存期必须与连接的生存期一样长。因此,要么将 CpropertiesViewSet 设为您对象的成员,它会在销毁时自行处理,要么在父对象的析构函数中调用 ClearEventCallback,要么在旧对象销毁之前将 SetEventCallback 设置为某个新对象。有几种可能的方法来设置连接的应用程序端。如果您有一个父对象有多个 CPropertiesViewSet 对象,请在 CPropertiesViewSet 中设置 ID,以便您可以在回调中区分 CPropertiesViewSet 对象。或者,为每个 CPropertiesViewSet 使用不同的回调。

每当您需要更新此集中的更改和/或希望将其设置为属性视图中的活动集时,请调用 ReloadProperties()

此类是 CPropertiesViewItem 对象的容器。

成员

void ReloadProperties( )
将此设为属性视图中的活动对象,并显示属性集的当前状态。
void AppendSet( PROPERTIESVIEW_ITEM& item ) 
CPropertiesViewItem 添加到此容器。
void ClearSet( ) 
从此容器中移除所有 CPropertiesViewItem 并清除视图(如果连接已激活)。
PROPERTIESVIEW_ITEM& GetItem( long item )
PROPERTIESVIEW_ITEM& operator [] ( long item ) 
返回此容器中的索引项。
void SetTitle( LPCTSTR t ) 
设置属性视图中使用的标题。使用 \n 分隔的字符串来创建标题的粗体部分和正常部分。“This is bold\nnormal text\n”。这是可选的,您可以使用普通字符串设置标题,例如“This Title”,它将全部为粗体。
void SetID( long nId ) 
设置此对象的 ID 值。(可选)
long GetID( ) 
获取此对象的 ID。
void SetGroup( long nGrp ) 
设置此对象的 Group 值。(可选)
long GetGroup( )
获取此对象的组号。
void SetEventCallback(  PVEVENTCALLBACK event, LPVOID object ) 
用于将您的父对象(例如视图或文档)连接到视图的事件触发器。当项被更改时,您的对象会收到通知,或者如果项中设置了 evUser、evUserNE 类型,则用户已按 alt/arrow_down 或鼠标单击了项按钮。
bool IsConnected( )
如果已连接到视图,则返回 true。没有对事件回调的检查,因为应该可以理解。
void ClearEventCallback( ) 
断开您的对象与回调的连接。内部使用,您可能永远不会使用它。它暴露出来是为了以防万一您需要它。

CPropertiesViewItem

将这些对象之一附加到 CPropertiesViewSet,对应于属性视图中的每个条目。视图项样式从头文件中的 Type 枚举设置。下拉列表当前使用 \n 分隔的字符串设置。可以为其他类型的列表添加方法。我没有向视图添加通用对话框,因为在应用程序端处理这类事情更有意义。添加通用对话框将意味着很多额外的开销,这些开销可能永远不会被使用,特别是由于对话框的特殊性。evUserevUserNE 类型将显示一个 (...) 按钮,您可以在回调中执行任何您喜欢的操作。树视图属性示例中对此进行了演示。NE 类型是“不可编辑”。如果需要,您可以随时更改类型。

成员

void SetType( Type t )

设置此项的类型。

  • evBool 显示一个真/假下拉列表。使用 SetIndex/GetIndex 设置/检索
  • evEdit 可编辑文本字段。
  • evDropDown 下拉选择
  • evUser (...) 按钮显示和一个可编辑字段。
  • evUserNE (...) 按钮显示和一个不可编辑字段。
SetLabel( LPCTSTR newLable )
设置在属性视图左侧列使用的标签。
void SetDescription( LPCTSTR newDescription )
设置当选中该项时在属性视图底部显示的文本。
SetList( LPCTSTR inList )
如果此项的类型是 evDropDown,则使用此项设置列表。列表以 \n 分隔,例如:“First\nSecond\nThird\n”。
void SetIndex( long i )
如果是下拉项,请使用此成员设置选择的索引。默认值为列表中的第一项。
SetStr( LPCTSTR edit )
这会设置在属性视图右侧显示的项文本。如果该项是具有项目的下拉列表,则显示将被覆盖。
long GetIndex( )
返回下拉项的当前选择索引。
LPCTSTR GetStr( )
获取右侧列值的字符串。

头文件

您只需要 PropertiesView.h 来创建属性视图。我已经将 CpropertiesViewSet 的实现拆分并放入了 PropertiesSet.h 头文件中。在示例中,如果我没有使用视图的 ConnectSet 成员,而只使用消息传递进行连接,那么我将不需要 PropertiesView.h。

示例项目

我将在此解释树视图连接。项目中还有更多示例。树视图属性全部在文档中完成,而列表视图则在视图中处理。我通过这两种方式进行了尝试,看看会遇到什么。文档中有一个名为 propTreeSetCpropertiesViewSet 成员。在 OnOpenDocument 中,该集使用以下方式初始化

    propTreeSet.SetTitle( _T("TreeView Properties") );

    PROPERTIESVIEW_ITEM item;
    item.SetType( CPropertiesViewItem::evUserNE );
    item.SetLabel( _T("Background Color") );
    item.SetDescription( _T("Set the Background color of the Tree View") );
    CString t;
    t.Format( _T("0x%8.8x"), colorTreeBkgd );
    t.MakeUpper( );
    item.SetStr( t );
    propTreeSet.AppendSet( item );

    item.SetType( CPropertiesViewItem::evDropDown );
    item.SetLabel( _T("Selection") );
    item.SetDescription( _T("Set the selection of the tree") );
    item.SetStr( _T("(empty set)") );
    propTreeSet.AppendSet( item );

我将为 CPropertiesViewSet 添加一个成员,使其能够识别 XML。这意味着您可以通过类似以下方式的调用来初始化该集

 propTreeSet.LoadXML( anXMLNode )
有一个 Doc 成员,它处理树获得焦点时的事件。因此,那时我知道视图是好的,并且第一次调用时,我可以进行两次连接。
void CSDIViewSwitchDoc::OnTreeViewFocus( )
{
    if( bLockTest )
        return;

    CPropertiesView* pPView= GetCPView( );
    if( pPView == NULL )
        return;

    if( !propTreeSet.IsConnected( ) )
    {
        //now there is a tree view so load the selection list
        CMyTreeView* pTreeView= ( get it from somewhere )
        CString t;
        pTreeView->GetTreeList( t );
        propTreeSet[TV_SELECT].SetList( t );
        propTreeSet.SetEventCallback( TreeCallback, this );
        pPView->ConnectSet( propTreeSet );
        //You don't need a pointer to the Properties View
        //Send this message instead
        AfxGetMainWnd( )->SendMessageToDescendants(
            ID_REG_PVM_CONNECT, (WPARAM)& propTreeSet )
    }
    else
        propTreeSet.ReloadProperies( );
}

如果您在运行时没有值,这通常是这样做的。视图列表是从树视图中获取的,以填充下拉列表。您可以按任意顺序执行 SetEventCallbackConnectSet。连接后,ReloadProperties 成员将使此集在视图中处于活动状态。

在这种情况下,事件回调被设为全局变量而不是文档成员。它以任何一种方式工作。

//These corospond to the index of the items in the CPropertiesViewSet
#define TV_SETCOLOR 0
#define TV_SELECT 1

bool CALLBACK TreeCallback( LPVOID object, int action, int item, int id )
{
    CSDIViewSwitchDoc* pDoc= static_cast<CSDIViewSwitchDoc*>( object );
    CMyTreeView* pTreeView= ( get it from somewhere );
    if( pTreeView == NULL )
        return false;

    switch( item )
    {
    case TV_SETCOLOR:
    {
        CColorDialog dlg( pDoc->colorTreeBkgd );
        if( dlg.DoModal( ) == IDOK )
        {
            CString t;
            pDoc->colorTreeBkgd= dlg.GetColor( );
            t.Format( _T("0x%8.8x"), pDoc->colorTreeBkgd );
            t.MakeUpper( );
            pDoc->propTreeSet[TV_SETCOLOR].SetStr( t );
            pTreeView->SendMessage( TVM_SETBKCOLOR, 0, pDoc->colorTreeBkgd );
            return true;
        }
        return false;
    }
    case TV_SELECT:
        pTreeView->SetSelected( pDoc->propTreeSet[TV_SELECT].GetIndex( ) );
        return true;
    }
    ASSERT( false );
    return false;
}

待办事项,下一版本

  • 弄清楚如何处理文档。
  • 允许在视图中设置工具栏。
  • CPropertiesViewSet 添加 XML 成员。

结论

从此进行的任何增强都不应破坏旧的实现。如果您测试 Unicode 构建,您将收到“数据损坏”错误,因为 SwitchSplit 不是为 Unicode 编写的。但是它会运行,并且我已经测试了足够长的时间,以至于该系统在 Unicode 中应该没问题。我修改了 SwitchSplit 应用程序以进行测试,但这看起来不会有问题。Form2 中滑块的更新在两个方向上都有效。非常欢迎反馈。

谢谢,Dan。

© . All rights reserved.