filter属性页

本篇文档我们将要讲述如何给一个filter创建一个属性页,通过CBasePropertyPage基类。这篇文档的实例代码演示了创建属性页的步骤,这里我们假设我们要创建属性页的视频filter支持饱和度属性页,这个属性页有一个滑动条,用户可以通过这个滑动条来控制饱和度。

第一步,设置属性的机理

Filter必须支持一种和属性页沟通的方式,通过属性页可以设置或者获取filter的属性,下面是可能的三种方式

1暴露一个接口

2通过IDispatch支持自动化属性

3暴露IPropertyBag 接口,并定义一系列的属性

下面的例子利用了一个普通的COM接口,叫做ISaturaton,这并不是一个真正的com接口,只是我们用来在这里举例的,你也可以自己定义任何的com对象。

首先我们在一个头文件中声明接口的ID和定义。

// Always create new GUIDs! Never copy a GUID from an example.

DEFINE_GUID(IID_ISaturation, 0x19412d6e, 0x6401,

0x475c, 0xb0, 0x48, 0x7a, 0xd2, 0x96, 0xe1, 0x6a, 0x19);

interface ISaturation : public IUnknown

{

STDMETHOD(GetSaturation)(long *plSat) = 0;

STDMETHOD(SetSaturation)(long lSat) = 0;

};

你也可以用IDL定义接口,并用MIDL编译器创建头文件,然后在Filter上实现这个接口,这个例子采用“Get”,“Set”方法来设置饱和度的值,注意,修改这个m_lSaturation的值的时候一定要进行保护

class CGrayFilter : public ISaturation, /* Other inherited classes. */

{

private:

CCritSec m_csShared; // Protects shared data.

long m_lSaturation; // Saturation level.

public:

STDMETHODIMP GetSaturation(long *plSat)

{

if (!plSat) return E_POINTER;

CAutoLock lock(&m_csShared);

*plSat = m_lSaturation;

return S_OK;

}

STDMETHODIMP SetSaturation(long lSat)

{

CAutoLock lock(&m_csShared);

if (lSat < SATURATION_MIN || lSat > SATURATION_MAX)

{

return E_INVALIDARG;

}

m_lSaturation = lSat;

return S_OK;

}

};

当然你实现接口的一些细节可能和上面的代码不一致。反正你自己实现就是了

第二步,实现ISpecifyPropertyPages接口

做完了上一步,下面就要在你个filter中实现ISpecifyPropertyPages接口,这个接口只有一个方法,GetPages,这个方法返回filter所支持的所有的属性页的CLSID。在这个例子里,Filter只支持一个属性页,

首先产生一个CLSID,并在头文件声明

// Always create new GUIDs! Never copy a GUID from an example.

DEFINE_GUID(CLSID_SaturationProp, 0xa9bd4eb, 0xded5,

0x4df0, 0xba, 0xf6, 0x2c, 0xea, 0x23, 0xf5, 0x72, 0x61);

然后要实现ISpecifyPropertyPages接口的GetPages方法:

class CGrayFilter : public ISaturation,

public ISpecifyPropertyPages,

/* Other inherited classes. */

{

public:

STDMETHODIMP GetPages(CAUUID *pPages)

{

if (pPages == NULL) return E_POINTER;

pPages->cElems = 1;

pPages->pElems = (GUID*)CoTaskMemAlloc(sizeof(GUID));

if (pPages->pElems == NULL)

{

return E_OUTOFMEMORY;

}

pPages->pElems[0] = CLSID_SaturationProp;

return S_OK;

}

};

/* ... */

}

第三步,支持QueryInterface

为了暴露Filter的接口,照着下面的步骤作哦

1 在你的filter中包含DECLARE_IUNKNOWN宏的声明:

Public:

DECLARE_IUNKNOWN;

2 重载CUnknown::NonDelegatingQueryInterface 方法来检查两个接口的IIDs。

STDMETHODIMP CGrayFilter::NonDelegatingQueryInterface(REFIID riid,

void **ppv)

{

if (riid == IID_ISpecifyPropertyPages)

{

return GetInterface(static_cast(this),

ppv);

}

if (riid == IID_ISaturation)

{

return GetInterface(static_cast(this), ppv);

}

return CBaseFilter::NonDelegatingQueryInterface(riid, ppv);

}

第四步,创建属性页

到这一步,filter已经支持一个属性页的所需要的东西了,下一步就是要实现属性页本身了。

首先创建一个对话框的资源,然后以这个对话的资源声明一个类,要从CBasePropertyPage. 派生,

图1

下面的代码显示了部分的声明,包含了我们在后面将要用到的部分变量。

class CGrayProp : public CBasePropertyPage

{

private:

ISaturation *m_pGray; // Pointer to the filter's custom interface.

long m_lVal // Store the old value, so we can revert.

long m_lNewVal; // New value.

public:

/* ... */

};

看看构造函数吧

CGrayProp::CGrayProp(IUnknown *pUnk) :

CBasePropertyPage(NAME("GrayProp"), pUnk, IDD_PROPPAGE, IDS_PROPPAGE_TITLE),

m_pGray(0)

{ }

下面,你还要记得重载CBasePropertyPage 的几个方法哦

OnConnect,当属性页创建的时候,会调用这个方法,通过这个方法将IUnknown指针付给Filter。

OnActivate 当对话框创建的时候被调用

OnReceiveMessage 当对话框接收到窗口消息时被调用

OnApplyChanges当用户单击OK或者Apply 按钮来确认对属性进行更新时,调用

OnDisconnect 当用户取消Property sheet时调用

第五步,保存filter的一个指针

通过重载CBasePropertyPage::OnConnect方法将一个指针保存到filter,下面的例子演示了如何通过方法传递过来的参数查询filter支持的接口

HRESULT CGrayProp::OnConnect(IUnknown *pUnk)

{

if (pUnk == NULL)

{

return E_POINTER;

}

ASSERT(m_pGray == NULL);

return pUnk->QueryInterface(IID_ISaturation,

reinterpret_cast(&m_pGray));

}

第六步,初始化对话框

通过重载CBasePropertyPage::OnActivate方法来初始化一个对话框,在这个例子里,属性页使用了滑动条,所以,在初始化的第一步就是要初始化控件动态库,然后再初始化slider。

HRESULT CGrayProp::OnActivate(void)

{

INITCOMMONCONTROLSEX icc;

icc.dwSize = sizeof(INITCOMMONCONTROLSEX);

icc.dwICC = ICC_BAR_CLASSES;

if (InitCommonControlsEx(&icc) == FALSE)

{

return E_FAIL;

}

ASSERT(m_pGray != NULL);

HRESULT hr = m_pGray->GetSaturation(&m_lVal);

if (SUCCEEDED(hr))

{

SendDlgItemMessage(m_Dlg, IDC_SLIDER1, TBM_SETRANGE, 0,

MAKELONG(SATURATION_MIN, SATURATION_MAX));

SendDlgItemMessage(m_Dlg, IDC_SLIDER1, TBM_SETTICFREQ,

(SATURATION_MAX - SATURATION_MIN) / 10, 0);

SendDlgItemMessage(m_Dlg, IDC_SLIDER1, TBM_SETPOS, 1, m_lVal);

}

return hr;

}

第七步,处理窗口消息

重载CBasePropertyPage::OnReceiveMessage方法来处理用户的输入等消息。如果你不想处理消息,你只需简单调用父类的OnReceiveMessage 即可。

无论何时用户改变了属性,都会做下面的事情

1 将属性页的m_bDirty设置为TRUE;

2调用属性框的IPropertyPageSite::OnStatusChange方法,并传递一个PROPPAGESTATUS_DIRTY,这个标志用来通知property frame应该将Apply按钮可用,

CBasePropertyPage::m_pPageSite变量保存着一个IPropertyPageSite接口

为了简化步骤,你可以在你的属性页中添加下面的代码

private:

void SetDirty()

{

m_bDirty = TRUE;

if (m_pPageSite)

{

m_pPageSite->OnStatusChange(PROPPAGESTATUS_DIRTY);

}

}

当用户改变了属性的时候,在OnReceiveMessage方法中调用上面的函数。

BOOL CGrayProp::OnReceiveMessage(HWND hwnd,

UINT uMsg, WPARAM wParam, LPARAM lParam)

{

switch (uMsg)

{

case WM_COMMAND:

if (LOWORD(wParam) == IDC_DEFAULT)

{

// User clicked the 'Revert to Default' button.

m_lNewVal = SATURATION_DEFAULT;

m_pGray->SetSaturation(m_lNewVal);

// Update the slider control.

SendDlgItemMessage(m_Dlg, IDC_SLIDER1, TBM_SETPOS, 1,

m_lNewVal);

SetDirty();

return (LRESULT) 1;

}

break;

case WM_HSCROLL:

{

// User moved the slider.

switch(LOWORD(wParam))

{

case TB_PAGEDOWN:

case SB_THUMBTRACK:

case TB_PAGEUP:

m_lNewVal = SendDlgItemMessage(m_Dlg, IDC_SLIDER1,

TBM_GETPOS, 0, 0);

m_pGray->SetSaturation(m_lNewVal);

SetDirty();

}

return (LRESULT) 1;

}

} // Switch.

// Let the parent class handle the message.

return CBasePropertyPage::OnReceiveMessage(hwnd,uMsg,wParam,lParam);

}

第八步,处理属性的改变

重载CBasePropertyPage::OnApplyChanges方法来提交属性页的改变,如果用户单击了确定,或者应用按钮,OnApplyChanges方法都会调用到

HRESULT CGrayProp::OnApplyChanges(void)

{

m_lVal = m_lNewVal;

return S_OK;

}

第九步,断开属性页连接

重载CBasePropertyPage::OnDisconnect方法来释放你在OnConnect方法中请求的所有的接口,如果用户没有更新属性,而是单击了取消按钮,你还要将属性的原始值保存下来。当用户单击取消按钮,但是没有相应的响应这个消息的方法,所以,你要检查用户是否调用了OnApplyChanges方法,看看例子也好:

HRESULT CGrayProp::OnDisconnect(void)

{

if (m_pGray)

{

// If the user clicked OK, m_lVal holds the new value.

// Otherwise, if the user clicked Cancel, m_lVal is the old value.

m_pGray->SetSaturation(m_lVal);

m_pGray->Release();

m_pGray = NULL;

}

return S_OK;

}

第十步,支持com的注册

最后一步就是要支持com的注册,因此 属性框才能够创建你属性页的实例,首先在全局数组g_Templates添加一个类厂模板的说明。这个全局的数组是你的DLL中创建的所有的com对象都要用到的。

const AMOVIESETUP_FILTER FilterSetupData =

{

/* Not shown ... */

};

CFactoryTemplate g_Templates[] =

{

// This entry is for the filter.

{

wszName,

&CLSID_GrayFilter,

CGrayFilter::CreateInstance,

NULL,

&FilterSetupData

},

// This entry is for the property page.

{

L"Saturation Props",

&CLSID_SaturationProp,

CGrayProp::CreateInstance,

NULL, NULL

}

};

如果你用下面的方式声明全局数组,数组的大小就会自动地得到修改

int g_cTemplates = sizeof(g_Templates)/sizeof(g_Templates[0]);

同时,还要在属性页类中添加一个CreateInstance方法

static CUnknown * WINAPI CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr)

{

CGrayProp *pNewObject = new CGrayProp(pUnk);

if (pNewObject == NULL)

{

*pHr = E_OUTOFMEMORY;

}

return pNewObject;

}

如果想测试属性页,可以注册DLL,然后将filter加载到GraphEdit,鼠标右击来查看filter的属性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值