WPF の双方向機能の概要
他の開発プラットフォームとは異なり、WPF には、双方向コンテンツの迅速な開発をサポートする多くの機能があります。たとえば、同じドキュメント内の左から右と右から左に混在するデータなどです。 同時に、WPF は、アラビア語やヘブライ語を話すユーザーなどの双方向機能を必要とするユーザーに優れたエクスペリエンスを作成します。
次のセクションでは、多くの双方向機能と、双方向コンテンツを最適に表示する方法を示す例について説明します。 ほとんどのサンプルでは XAML を使用しますが、C# または Microsoft Visual Basic コードに概念を簡単に適用できます。
FlowDirection
WPF アプリケーションのコンテンツ フローの方向を定義する基本的なプロパティは、FlowDirectionです。 このプロパティは、LeftToRight または RightToLeftの 2 つの列挙値のいずれかに設定できます。 このプロパティは、FrameworkElementから継承するすべての WPF 要素で使用できます。
次の例では、TextBox 要素のフロー方向を設定します。
左から右方向の流れ
<TextBlock Background="DarkBlue" Foreground="LightBlue"
FontSize="20" FlowDirection="LeftToRight">
This is a left-to-right TextBlock
</TextBlock>
右から左へのフロー方向
<TextBlock Background="LightBlue" Foreground="DarkBlue"
FontSize="20" FlowDirection="RightToLeft">
This is a right-to-left TextBlock
</TextBlock>
次の図は、前のコードがどのようにレンダリングされるかを示しています。
ユーザー インターフェイス (UI) ツリー内の要素は、そのコンテナーから FlowDirection を継承します。 次の例では、TextBlock は Gridに存在し、その Gridは Windowにあります。 Window の FlowDirection を設定することは、Grid と TextBlock にも設定を意味します。
次の例では、FlowDirectionの設定を示します。
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="FlowDirectionApp.Window1"
Title="BidiFeatures" Height="200" Width="700"
FlowDirection="RightToLeft">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" >
This is a right-to-left TextBlock
</TextBlock>
<TextBlock Grid.Column="1" FlowDirection="LeftToRight">
This is a left-to-right TextBlock
</TextBlock>
</Grid>
</Window>
最上位レベル Window には RightToLeftFlowDirectionがあるため、その中に含まれるすべての要素も同じ FlowDirectionを継承します。 要素が指定した FlowDirection をオーバーライドするには、前の例の 2 番目の TextBlockLeftToRightに変更するなど、明示的な方向変更を追加する必要があります。 FlowDirection が定義されていない場合、既定の LeftToRight が適用されます。
次の図は、前の例の出力を示しています。
フロウドキュメント
HTML、Win32、Java などの多くの開発プラットフォームでは、双方向コンテンツ開発を特別にサポートしています。 HTML などのマークアップ言語は、コンテンツ ライターに、必要な方向にテキストを表示するために必要なマークアップを提供します。たとえば、HTML 4.0 タグ、"dir" ("rtl" または "ltr" を値として受け取る) などです。 このタグは FlowDirection プロパティに似ていますが、FlowDirection プロパティはテキスト コンテンツのレイアウトをより高度な方法で動作し、テキスト以外のコンテンツに使用できます。
テキスト、テーブル、イメージ、およびその他の要素の組み合わせをホストできる UI 要素。 次のセクションのサンプルでは、この要素を使用します。
FlowDocument へのテキストの追加は、それ以上の方法で行うことができます。 これを行う簡単な方法は、テキストなどのコンテンツをグループ化するために使用されるブロック レベルの要素である Paragraph を使用することです。 インライン レベルの要素にテキストを追加するには、サンプルで Span と Runを使用します。 Span は、他のインライン要素をグループ化するために使用されるインラインレベルのフロー コンテンツ要素ですが、Run は書式設定されていないテキストの実行を含むインラインレベルのフロー コンテンツ要素です。 Span には、複数の Run 要素を含めることができます。
最初のドキュメントの例には、多数のネットワーク共有名を持つドキュメントが含まれています。たとえば、\\server1\folder\file.ext
. アラビア語または英語のドキュメントにこのネットワーク リンクがある場合でも、常に同じ方法で表示する必要があります。 次の図は、Span 要素の使用方法を示し、アラビア語の RightToLeft ドキュメントのリンクを示しています。
テキストは RightToLeftされているため、"\" などの特殊文字はすべて右から左の順序で区切ります。 その結果、リンクが正しい順序で表示されないので、問題を解決するには、テキストを埋め込んで、流れる別の RunLeftToRightを保持する必要があります。 各言語に個別の Run を持つ代わりに、問題を解決するより良い方法は、使用頻度の低い英語のテキストを大きなアラビア語の Spanに埋め込むことです。
次の図は、Span 要素に埋め込まれた Run 要素を使用してこれを示しています。
次の例では、ドキュメントで Run 要素と Span 要素を使用する方法を示します。
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
FlowDirection="RightToLeft">
<FlowDocument>
<Paragraph>
<Span FlowDirection="RightToLeft" >
ستجد الملف هنا:
<Run FlowDirection="LeftToRight">
\\server1\filename\filename1.txt</Run>
ثم باقى النص!
</Span>
</Paragraph>
</FlowDocument>
</Page>
Span 要素
Span 要素は、フロー方向が異なるテキスト間の境界区切り記号として機能します。 フロー方向が同じ Span 要素でも、双方向スコープが異なると見なされます。つまり、Span 要素はコンテナーの FlowDirectionで並べ替えられ、Span 要素内のコンテンツのみが Spanの FlowDirection に従います。
次の図は、複数の TextBlock 要素の流れの方向を示しています。
次の例は、Span 要素と Run 要素を使用して、前の図に示した結果を生成する方法を示しています。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<StackPanel >
<TextBlock FontSize="20" FlowDirection="RightToLeft">
<Run FlowDirection="LeftToRight">العالم</Run>
<Run FlowDirection="LeftToRight" Foreground="Red" >فى سلام</Run>
</TextBlock>
<TextBlock FontSize="20" FlowDirection="LeftToRight">
<Run FlowDirection="RightToLeft">العالم</Run>
<Run FlowDirection="RightToLeft" Foreground="Red" >فى سلام</Run>
</TextBlock>
<TextBlock FontSize="20" Foreground="Blue">العالم فى سلام</TextBlock>
<Separator/>
<TextBlock FontSize="20" FlowDirection="RightToLeft">
<Span Foreground="Red" FlowDirection="LeftToRight">Hello</Span>
<Span FlowDirection="LeftToRight">World</Span>
</TextBlock>
<TextBlock FontSize="20" FlowDirection="LeftToRight">
<Span Foreground="Red" FlowDirection="RightToLeft">Hello</Span>
<Span FlowDirection="RightToLeft">World</Span>
</TextBlock>
<TextBlock FontSize="20" Foreground="Blue">Hello World</TextBlock>
</StackPanel>
</Page>
サンプルの TextBlock 要素では、Span 要素は親の FlowDirection に従ってレイアウトされますが、各 Span 要素内のテキストは独自の FlowDirectionに従って流れます。 これは、ラテン語とアラビア語、またはその他の言語に適用されます。
xml:lang の追加
次の図は、数値と算術式 ("200.0+21.4=221.4"
など) を使用する別の例を示しています。 FlowDirection のみが設定されていることに注意してください。
このアプリケーションのユーザーは、FlowDirection が正しいにもかかわらず、数字がアラビア数字の形式に整形されていないため、出力にがっかりします。
XAML 要素には、各要素の言語を定義する XML 属性 (xml:lang
) を含めることができます。 XAML では、ツリー内の親要素に適用 xml:lang
値が子要素によって使用される XML 言語原則もサポートされています。 前の例では、Run 要素またはその最上位要素に言語が定義されていないため、既定の xml:lang
が使用されました。これは XAML に en-US
されています。 Windows Presentation Foundation (WPF) の内部数値整形アルゴリズムは、対応する言語 (この場合は英語) で数値を選択します。 アラビア数字を正しく表示するには、xml:lang
を設定する必要があります。
次の図は、xml:lang
が追加された例を示しています。
次の例では、アプリケーションに xml:lang
を追加します。
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
FlowDirection="RightToLeft">
<FlowDocument>
<Paragraph>
<Span FlowDirection="RightToLeft" Language="ar-SA">
العملية الحسابية: "200.0+21.4=221.4"
</Span>
</Paragraph>
</FlowDocument>
</Page>
多くの言語は、対象となる地域によって異なる xml:lang
値を持つことに注意してください。たとえば、"ar-SA"
と "ar-EG"
はアラビア語の 2 つのバリエーションを表します。 前の例では、xml:lang
値と FlowDirection 値の両方を定義する必要があることを示しています。
テキスト以外の要素を含む FlowDirection
FlowDirection では、テキスト要素内のテキストの流れ方だけでなく、ほぼすべての UI 要素のフロー方向も定義します。 次の図は、水平方向の LinearGradientBrush を使用して背景を左から右のグラデーションで描画する ToolBar を示しています。
FlowDirection を RightToLeftに設定すると、ToolBar ボタンは右から左に配置されるだけでなく、LinearGradientBrush でもオフセットが右から左に再調整されます。
次の図は、LinearGradientBrushの再調整を示しています。
次の例では、RightToLeftToolBarを描画します。 (左から右に描画するには、ToolBarの FlowDirection 属性を削除します。
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ToolBar FlowDirection="RightToLeft" Height="50" DockPanel.Dock="Top">
<ToolBar.Background>
<LinearGradientBrush StartPoint="0,0.5" EndPoint="1,1">
<LinearGradientBrush.GradientStops>
<GradientStop Color="DarkRed" Offset="0" />
<GradientStop Color="DarkBlue" Offset="0.3" />
<GradientStop Color="LightBlue" Offset="0.6" />
<GradientStop Color="White" Offset="1" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</ToolBar.Background>
<Button FontSize="12" Foreground="White">Button1</Button>
<Rectangle Width="20"/>
<Button FontSize="12" Foreground="White">Button2</Button>
<Rectangle Width="20"/>
<Button FontSize="12" Foreground="White">Button3</Button>
<Rectangle Width="20"/>
<Button FontSize="12" Foreground="White">Button4</Button>
<Rectangle Width="20"/>
</ToolBar>
</Page>
FlowDirection関連の例外
FlowDirection が期待どおりに動作しない場合がいくつかあります。 このセクションでは、次の 2 つの例外について説明します。
イメージ
Image は、イメージを表示するコントロールを表します。 XAML では、表示する Image の URI (Uniform Resource Identifier) を定義する Source プロパティと共に使用できます。
他の UI 要素とは異なり、Image はコンテナーから FlowDirection を継承しません。 ただし、FlowDirection が明示的に RightToLeftに設定されている場合、Image は水平方向に反転して表示されます。 これは、双方向コンテンツの開発者にとって便利な機能として実装されています。場合によっては、画像を水平方向に反転すると目的の効果が得られるからです。
次の図は、反転された Imageを示しています。
次の例は、Image が FlowDirection を含む StackPanel から継承できない場合を示しています。
手記
この例を実行するには、C:\ ドライブに ms_logo.jpg という名前のファイルが必要です。
<StackPanel
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
FlowDirection="RightToLeft">
<Image Source="file://c:/ms_logo.jpg"
Width="147" Height="50"/>
<Separator Height="10"/>
<Image Source="file://c:/ms_logo.jpg"
Width="147" Height="50" FlowDirection="LeftToRight" />
<Separator Height="10"/>
<Image Source="file://c:/ms_logo.jpg"
Width="147" Height="50" FlowDirection="RightToLeft"/>
</StackPanel>
手記
ダウンロード ファイルには、ms_logo.jpg ファイルが含まれています。 このコードでは、.jpg ファイルがプロジェクト内ではなく C:\ ドライブのどこかにあることを前提としています。 プロジェクト ファイルから C:\ ドライブに .jpg をコピーするか、コードを変更してプロジェクト内のファイルを検索する必要があります。 これを行うには、Source="file://c:/ms_logo.jpg"
を Source="ms_logo.jpg"
に変更します。
パス
Imageに加えて、もう一つの興味深い要素は Pathです。 Path は、接続された一連の線と曲線を描画できるオブジェクトです。 これは、その FlowDirectionに関する Image に似た方法で動作します。たとえば、RightToLeftFlowDirection は LeftToRight 1 の水平ミラーです。 ただし、Imageとは異なり、Path はコンテナーから FlowDirection を継承し、明示的に指定する必要はありません。
次の例では、3 本の線を使用して単純な矢印を描画します。 最初の矢印は、StackPanel から RightToLeft のフロー方向を継承し、その始点と終点が右側の基準点から測定されるように設定されます。 明示的な RightToLeftFlowDirection を持つ 2 番目の矢印も右側から始まります。 ただし、3 番目の矢印の左側には開始ルートがあります。 描画の詳細については、「LineGeometry と GeometryGroup」を参照してください。
<StackPanel
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
FlowDirection="RightToLeft">
<Path Stroke="Blue" StrokeThickness="4">
<Path.Data>
<GeometryGroup >
<LineGeometry StartPoint="300,10" EndPoint="350,30" />
<LineGeometry StartPoint="10,30" EndPoint="352,30" />
<LineGeometry StartPoint="300,50" EndPoint="350,30" />
</GeometryGroup>
</Path.Data>
</Path>
<Path Stroke="Red" StrokeThickness="4" FlowDirection="RightToLeft">
<Path.Data>
<GeometryGroup >
<LineGeometry StartPoint="300,10" EndPoint="350,30" />
<LineGeometry StartPoint="10,30" EndPoint="352,30" />
<LineGeometry StartPoint="300,50" EndPoint="350,30" />
</GeometryGroup>
</Path.Data>
</Path>
<Path Stroke="Green" StrokeThickness="4" FlowDirection="LeftToRight">
<Path.Data>
<GeometryGroup >
<LineGeometry StartPoint="300,10" EndPoint="350,30" />
<LineGeometry StartPoint="10,30" EndPoint="352,30" />
<LineGeometry StartPoint="300,50" EndPoint="350,30" />
</GeometryGroup>
</Path.Data>
</Path>
</StackPanel>
次の図は、Path
要素を使用して矢印が描画された前の例の出力を示しています。
Image と Path は、WPF が FlowDirectionを使用する方法の 2 つの例です。 コンテナー内の特定の方向に UI 要素をレイアウトする以外に、FlowDirection は、サーフェス、LinearGradientBrush、RadialGradientBrushにインクをレンダリングする InkPresenter などの要素と共に使用できます。 左から右の動作を模倣するコンテンツの右から左への動作が必要な場合、またはその逆が必要な場合は常に、Windows Presentation Foundation (WPF) でその機能が提供されます。
数値の置換
これまで、Windows では、同じ数字に対して異なるカルチャ図形を表現できるようにすることで数値の置換がサポートされていましたが、これらの数字の内部ストレージはさまざまなロケール間で統一され、たとえば、数値は既知の 16 進値 (0x40、0x41) に格納されますが、選択した言語に従って表示されます。
これにより、アプリケーションは、ある言語から別の言語に変換することなく数値を処理できます。たとえば、ユーザーはローカライズされたアラビア語の Windows で Microsoft Excel スプレッドシートを開き、アラビア語で整形された数字を表示できますが、ヨーロッパ版の Windows で開き、同じ数値のヨーロッパの表現を表示できます。 これは、コンマ区切り記号やパーセント記号などの他の記号にも必要です。これは、通常、同じドキュメント内の数値に付随するためです。
Windows Presentation Foundation (WPF) は引き続き同じ伝統を持ち、置換のタイミングと使用方法をよりユーザーが制御できるようにするこの機能のサポートをさらに追加します。 この機能は任意の言語向けに設計されていますが、双方向コンテンツでは特に便利です。アプリケーションが実行される可能性のあるさまざまなカルチャのため、通常、特定の言語の数字を整形することはアプリケーション開発者にとって困難です。
Windows Presentation Foundation (WPF) での数値置換の動作を制御するコア プロパティは、Substitution 依存関係プロパティです。 NumberSubstitution クラスは、テキスト内の数値を表示する方法を指定します。 動作を定義する 3 つのパブリック プロパティがあります。 各プロパティの概要を次に示します。
カルチャーソース:
このプロパティは、数値のカルチャを決定する方法を指定します。 3 つの NumberCultureSource 列挙値のいずれかを受け取ります。
オーバーライド: 数値カルチャは CultureOverride プロパティに属しています。
テキスト: 数値カルチャは、テキスト ランのカルチャです。 マークアップでは、これは
xml:lang
、またはそのエイリアスLanguage
プロパティ (Language または Language) になります。 また、FrameworkContentElementから派生するクラスの既定値です。 このようなクラスには、System.Windows.Documents.Paragraph、System.Windows.Documents.Table、System.Windows.Documents.TableCell などがあります。ユーザー: 数値カルチャは、現在のスレッドのカルチャです。 このプロパティは、Page、Window、TextBlockなど、FrameworkElement のすべてのサブクラスの既定値です。
CultureOverride:
CultureOverride プロパティは、CultureSource プロパティが Override に設定され、それ以外の場合は無視される場合にのみ使用されます。 数値カルチャを指定します。 既定値である null
の値は、en-USとして解釈されます。
置換:
このプロパティは、実行する数値置換の種類を指定します。 次のいずれかの NumberSubstitutionMethod 列挙値を受け取ります。
AsCulture: 置換方法は、数値カルチャの NumberFormatInfo.DigitSubstitution プロパティに基づいて決定されます。 これが既定値です。
Context: 数字カルチャがアラビア語またはペルシャ語のカルチャの場合、数字がコンテキストに依存することを指定します。
European: 数値は常にヨーロッパの数字としてレンダリングされます。
NativeNational: 数値は、そのカルチャの NumberFormatで指定された数値文化の国番号を使用して表示されます。
Traditional: 数値は、数値カルチャの従来の数字を使用してレンダリングされます。 ほとんどの文化では、これは NativeNationalと同じです。 ただし、NativeNationalは一部のアラビア文化ではラテン数字になりますが、この値はすべてのアラビア文化ではアラビア数字になります。
双方向コンテンツ開発者にとって、これらの値は何を意味しますか? ほとんどの場合、開発者は、FlowDirection と各テキスト UI 要素の言語を定義するだけで済む場合があります (たとえば、Language="ar-SA"
と NumberSubstitution ロジックは、正しい UI に従って数値を表示します。 次の例では、アラビア語バージョンの Windows で実行されている Windows Presentation Foundation (WPF) アプリケーションでアラビア語と英語の数字を使用する方法を示します。
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<StackPanel>
<TextBlock Background="LightGreen" FontSize="32"
Language="ar-SA" FlowDirection="RightToLeft">1+2=3</TextBlock>
<TextBox Background="LightGreen" FontSize="32"
Language="ar-SA" FlowDirection="RightToLeft">1+2=3</TextBox>
<TextBlock Background="LightBlue" FontSize="32">1+2=3</TextBlock>
<TextBox Background="LightBlue" FontSize="32">1+2=3</TextBox>
</StackPanel>
</Page>
次の図は、アラビア語と英語の数字が表示されたアラビア語バージョンの Windows で実行している場合の、前のサンプルの出力を示しています。
FlowDirection を LeftToRight に設定するとヨーロッパの数字が生成されるため、この場合は FlowDirection が重要でした。 次のセクションでは、文書全体で数字を統一表示する方法について説明します。 この例がアラビア語の Windows で実行されていない場合、すべての数字はヨーロッパの数字として表示されます。
置換規則の定義
実際のアプリケーションでは、プログラムで言語を設定する必要がある場合があります。 たとえば、xml:lang
属性をシステムの UI で使用される属性と同じに設定したり、アプリケーションの状態に応じて言語を変更したりできます。
アプリケーションの状態に基づいて変更を加える場合は、Windows Presentation Foundation (WPF) によって提供されるその他の機能を使用します。
まず、アプリケーション コンポーネントの NumberSubstitution.CultureSource="Text"
を設定します。 この設定を使用すると、TextBlockなど、"User" が既定値であるテキスト要素の UI から設定が取得されないようにします。
例えば:
<TextBlock
Name="text1" NumberSubstitution.CultureSource="Text">
1234+5679=6913
</TextBlock>
対応する C# コードで、たとえば、Language
プロパティを "ar-SA"
に設定します。
text1.Language = System.Windows.Markup.XmlLanguage.GetLanguage("ar-SA");
Language
プロパティを現在のユーザーの UI 言語に設定する必要がある場合は、次のコードを使用します。
text1.Language = System.Windows.Markup.XmlLanguage.GetLanguage(System.Globalization.CultureInfo.CurrentUICulture.IetfLanguageTag);
CultureInfo.CurrentCulture は、実行時に現在のスレッドによって使用される現在のカルチャを表します。
最終的な XAML の例は、次の例のようになります。
<Page x:Class="WindowsApplication.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Code Sample" Height="300" Width="300"
>
<StackPanel>
<TextBlock Language="ar-SA"
FlowDirection="RightToLeft">عربى: 1+2=3
</TextBlock>
<TextBlock Language="ar-SA"
FlowDirection="RightToLeft"
NumberSubstitution.Substitution="European">عربى: 1+2=3
</TextBlock>
</StackPanel>
</Page>
最終的な C# の例は、次のようになります。
namespace BidiTest
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
string currentLanguage =
System.Globalization.CultureInfo.CurrentCulture.IetfLanguageTag;
text1.Language = System.Windows.Markup.XmlLanguage.GetLanguage(currentLanguage);
if (currentLanguage.ToLower().StartsWith("ar"))
{
text1.FlowDirection = FlowDirection.RightToLeft;
}
else
{
text1.FlowDirection = FlowDirection.LeftToRight;
}
}
}
}
次の図は、どちらのプログラミング言語でも、アラビア数字を表示するウィンドウの外観を示しています。
代入プロパティ の使用
Windows Presentation Foundation (WPF) での数値置換の動作方法は、テキスト要素の言語とその FlowDirectionの両方によって異なります。 FlowDirection が左から右の場合、ヨーロッパの数字がレンダリングされます。 ただし、前にアラビア語のテキストが付いている場合、または言語が "ar" に設定されていて、FlowDirection が RightToLeftされている場合は、代わりにアラビア数字がレンダリングされます。
ただし、すべてのユーザーに対してヨーロッパの数字などの統合アプリケーションを作成したい場合もあります。 特定の Styleを持つ Table セル内のアラビア数字。 これを行う簡単な方法の 1 つは、Substitution プロパティを使用する方法です。
次の例では、最初の TextBlock には Substitution プロパティが設定されていないため、アルゴリズムはアラビア数字を想定どおりに表示します。 ただし、2 番目の TextBlockでは、置換がヨーロッパに設定され、アラビア数字の既定の置換がオーバーライドされ、ヨーロッパの数字が表示されます。
<Page x:Class="WindowsApplication.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Code Sample" Height="300" Width="300"
>
<StackPanel>
<TextBlock Language="ar-SA"
FlowDirection="RightToLeft">عربى: 1+2=3
</TextBlock>
<TextBlock Language="ar-SA"
FlowDirection="RightToLeft"
NumberSubstitution.Substitution="European">عربى: 1+2=3
</TextBlock>
</StackPanel>
</Page>
.NET Desktop feedback