我使用Delphi和C ++ Builder,并且对于屏幕阅读器有一些可访问性问题。
我在表格上有一个按钮,它来自TWinControl。如果我在按钮上添加了标题,当按钮处于焦点时,屏幕阅读器将向我显示。但是,有些情况下我使用带图像的按钮而没有标题。如果没有标题,屏幕阅读器不会说任何内容。如果让屏幕阅读器说出这个按钮是什么,我该怎么办?
类似地,对于从TGraphicControl下降的表单上的图像。如何在对象被鼠标悬停时告诉屏幕阅读器说什么?
我已经查看了IAccessible包装器,但是如果可能的话,我宁愿不扩展我们使用的每个控件。
答案 0 :(得分:3)
但是,有些情况下我使用带图像的按钮而没有标题。如果没有标题,屏幕阅读器不会说任何内容。如果让屏幕阅读器说出这个按钮是什么,我该怎么办?
按钮的IAccessible
实现必须为屏幕阅读器提供所需的文本。默认情况下,操作系统为许多UI控件提供默认的IAccessible
实现,包括按钮。
所以,你可以做的一个简单技巧是手动拥有 - 绘制按钮,然后你可以为默认Caption
实现设置标准IAccessible
以便正常使用,然后你可以简单地在绘制按钮时不要包含Caption
。
否则,您可以直接处理WM_GETOBJECT
消息以检索按钮的默认IAccessible
实现,然后将其包装,以便您可以返回所需的文本并将其他所有内容委托给默认实现。例如:
type
TMyAccessibleText = class(TInterfacedObject, IAccessible)
private
fAcc: IAccessible;
fAccessibleText: string;
public:
constructor Create(Acc: IAccessible; AccessibleText: string);
function QueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall;
function GetTypeInfoCount(out Count: Integer): HResult; stdcall;
function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
function GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
function Get_accParent(out ppdispParent: IDispatch): HResult; stdcall;
function Get_accChildCount(out pcountChildren: Integer): HResult; stdcall;
function Get_accChild(varChild: OleVariant; out ppdispChild: IDispatch): HResult; stdcall;
function Get_accName(varChild: OleVariant; out pszName: WideString): HResult; stdcall;
function Get_accValue(varChild: OleVariant; out pszValue: WideString): HResult; stdcall;
function Get_accDescription(varChild: OleVariant; out pszDescription: WideString): HResult; stdcall;
function Get_accRole(varChild: OleVariant; out pvarRole: OleVariant): HResult; stdcall;
function Get_accState(varChild: OleVariant; out pvarState: OleVariant): HResult; stdcall;
function Get_accHelp(varChild: OleVariant; out pszHelp: WideString): HResult; stdcall;
function Get_accHelpTopic(out pszHelpFile: WideString; varChild: OleVariant; out pidTopic: Integer): HResult; stdcall;
function Get_accKeyboardShortcut(varChild: OleVariant; out pszKeyboardShortcut: WideString): HResult; stdcall;
function Get_accFocus(out pvarChild: OleVariant): HResult; stdcall;
function Get_accSelection(out pvarChildren: OleVariant): HResult; stdcall;
function Get_accDefaultAction(varChild: OleVariant; out pszDefaultAction: WideString): HResult; stdcall;
function accSelect(flagsSelect: Integer; varChild: OleVariant): HResult; stdcall;
function accLocation(out pxLeft: Integer; out pyTop: Integer; out pcxWidth: Integer; out pcyHeight: Integer; varChild: OleVariant): HResult; stdcall;
function accNavigate(navDir: Integer; varStart: OleVariant; out pvarEndUpAt: OleVariant): HResult; stdcall;
function accHitTest(xLeft: Integer; yTop: Integer; out pvarChild: OleVariant): HResult; stdcall;
function accDoDefaultAction(varChild: OleVariant): HResult; stdcall;
function Set_accName(varChild: OleVariant; const pszName: WideString): HResult; stdcall;
function Set_accValue(varChild: OleVariant; const pszValue: WideString): HResult; stdcall;
end;
constructor TMyAccessibleText.Create(Acc: IAccessible; AccessibleText: string);
begin
inherited Create;
fAcc := Acc;
fAccessibleText := AccessibleText;
end;
function TMyAccessibleText.QueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall;
begin
if IID = IID_IAccessible then
Result := inherited QueryInterface(IID, Obj)
else
Result := fAcc.QueryInterface(IID, Obj);
end;
function TMyAccessibleText.GetTypeInfoCount(out Count: Integer): HResult; stdcall;
begin
Result := fAcc.GetTypeInfoCount(Count);
end;
function TMyAccessibleText.GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall;
begin
Result := fAcc.GetTypeInfo(Index, LocaleID, TypeInfo);
end;
function TMyAccessibleText.GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall;
begin
Result := fAcc.GetIDsOfNames(IID, Names, NameCount, LocaleID, DispIDs);
end;
function TMyAccessibleText.Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
begin
Result := fAcc.Invoke(DispID, IID, LocaleID, Flags, Params, VarResult, ExcepInfo, ArgErr);
end;
function TMyAccessibleText.Get_accParent(out ppdispParent: IDispatch): HResult; stdcall;
begin
Result := fAcc.Get_accParent(ppdispParent);
end;
function TMyAccessibleText.Get_accChildCount(out pcountChildren: Integer): HResult; stdcall;
begin
Result := fAcc.Get_accChildCount(pcountChildren);
end;
function TMyAccessibleText.Get_accChild(varChild: OleVariant; out ppdispChild: IDispatch): HResult; stdcall;
begin
Result := fAcc.Get_accChild(varChild, ppdispChild);
end;
function TMyAccessibleText.Get_accName(varChild: OleVariant; out pszName: WideString): HResult; stdcall;
begin
Result := fAcc.Get_accName(varChild, pszName);
end;
function TMyAccessibleText.Get_accValue(varChild: OleVariant; out pszValue: WideString): HResult; stdcall;
begin
if varChild = CHILDID_SELF then
begin
pszValue := fAccessibleText;
Result := S_OK;
end else
Result := fAcc.Get_accValue(varChild, pszValue);
end;
function TMyAccessibleText.Get_accDescription(varChild: OleVariant; out pszDescription: WideString): HResult; stdcall;
begin
Result := fAcc.Get_accDescription(varChild, pszDescription);
end;
function TMyAccessibleText.Get_accRole(varChild: OleVariant; out pvarRole: OleVariant): HResult; stdcall;
begin
Result := fAcc.Get_accRole(varChild, pvarRole);
end;
function TMyAccessibleText.Get_accState(varChild: OleVariant; out pvarState: OleVariant): HResult; stdcall;
begin
Result := fAcc.Get_accState(varChild, pvarState);
end;
function TMyAccessibleText.Get_accHelp(varChild: OleVariant; out pszHelp: WideString): HResult; stdcall;
begin
Result := fAcc.Get_accHelp(varChild, pszHelp);
end;
function TMyAccessibleText.Get_accHelpTopic(out pszHelpFile: WideString; varChild: OleVariant; out pidTopic: Integer): HResult; stdcall;
begin
Result := fAcc.Get_accHelpTopic(pszHelpFile, varChild, pidTopic);
end;
function TMyAccessibleText.Get_accKeyboardShortcut(varChild: OleVariant; out pszKeyboardShortcut: WideString): HResult; stdcall;
begin
Result := fAcc.Get_accKeyboardShortcut(varChild, pszKeyboardShortcut);
end;
function TMyAccessibleText.Get_accFocus(out pvarChild: OleVariant): HResult; stdcall;
begin
Result := fAcc.Get_accFocus(pvarChild);
end;
function TMyAccessibleText.Get_accSelection(out pvarChildren: OleVariant): HResult; stdcall;
begin
Result := fAcc.Get_accSelection(pvarChildren);
end;
function TMyAccessibleText.Get_accDefaultAction(varChild: OleVariant; out pszDefaultAction: WideString): HResult; stdcall;
begin
Result := fAcc.Get_accDefaultAction(varChild, pszDefaultAction);
end;
function TMyAccessibleText.accSelect(flagsSelect: Integer; varChild: OleVariant): HResult; stdcall;
begin
Result := fAcc.accSelect(flagsSelect, varChild);
end;
function TMyAccessibleText.accLocation(out pxLeft: Integer; out pyTop: Integer; out pcxWidth: Integer; out pcyHeight: Integer; varChild: OleVariant): HResult; stdcall;
begin
Result := fAcc.accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, varChild);
end;
function TMyAccessibleText.accNavigate(navDir: Integer; varStart: OleVariant; out pvarEndUpAt: OleVariant): HResult; stdcall;
begin
Result := fAcc.accNavigate(navDir, varStart, pvarEndUpAt);
end;
function TMyAccessibleText.accHitTest(xLeft: Integer; yTop: Integer; out pvarChild: OleVariant): HResult; stdcall;
begin
Result := fAcc.accHitTest(xLeft, yTop, pvarChild);
end;
function TMyAccessibleText.accDoDefaultAction(varChild: OleVariant): HResult; stdcall;
begin
Result := fAcc.accDoDefaultAction(varChild);
end;
function TMyAccessibleText.Set_accName(varChild: OleVariant; const pszName: WideString): HResult; stdcall;
begin
Result := fAcc.Set_accName(varChild, pszName);
end;
function TMyAccessibleText.Set_accValue(varChild: OleVariant; const pszValue: WideString): HResult; stdcall;
begin
if varChild = CHILDID_SELF then
begin
fAccessibleText := pszValue;
Result := S_OK;
end else
Result := fAcc.Set_accValue(varChild, pszValue);
end;
type
TBitBtn = class(Vcl.Buttons.TBitBtn)
private
procedure WMGetObject(var Message: TMessage): message WM_GETOBJECT;
public
MyAccessibleText: string;
end;
TMyForm = class(TForm)
Button1: TBitBtn;
...
procedure FormCreate(Sender: TObject);
...
end;
procedure TMyForm.FormCreate(Sender: TObject);
begin
Button1.MyAccessibleText := 'There is an image here';
end;
procedure TBitBtn.WMGetObject(var Message: TMessage);
var
Acc: IAccessible;
begin
inherited;
if (Message.LParam = OBJID_CLIENT) and (Message.Result > 0) and (Caption = '') and (MyAccessibleText <> '') then
begin
if ObjectFromLresult(Message.Result, IAccessible, Message.WParam, Acc) = S_OK then
begin
Acc := TMyAccessibleText.Create(Acc, MyAccessibleText) as IAccessible;
Message.Result := LresultFromObject(IAccessible, Message.WParam, Acc);
end;
end;
end;
类似地,对于从TGraphic下降的表格上的图像。如何在对象获得焦点时告诉屏幕阅读器说什么?
首先,TGraphic
不是组件类。它是TPicture
使用的图像数据的包装器,它本身就是TImage
使用的帮助器。我认为你的意思是TGraphicControl
而不是TImage
来自。{/ p>
默认情况下,屏幕阅读器无法直接访问基于TGraphicControl
的组件,因为它没有自己的窗口,因此操作系统本身甚至都不知道。
如果您希望屏幕阅读器与图形控件进行交互,则必须从IAccessible
组件(具有窗口)提供Parent
的完整实现,并使其显示其他辅助功能信息关于它的图形孩子。
我已经查看了IAccessible包装器,但是如果可能的话,我宁愿不扩展我们使用的每个控件。
很抱歉,但您必须(除非您能找到满足您需求的第三方实施)。 VCL根本没有实现任何IAccessible
功能,因此如果您需要在操作系统为您提供的范围之外进行自定义,则必须在您自己的代码中手动实现它。