Partilhar via


Bidirectional Features in WPF Overview

Unlike any other development platform, WPF has many features that support rapid development of bidirectional content, for example, mixed left to right and right to left data in the same document. At the same time, WPF creates an excellent experience for users who require bidirectional features such as Arabic and Hebrew speaking users.

The following sections explain many bidirectional features together with examples illustrating how to achieve the best display of bidirectional content. Most of the samples use XAML, though you can easily apply the concepts to C# or Microsoft Visual Basic code.

Este tópico contém as seguintes seções.

  • FlowDirection
  • FlowDocument
  • Span Elements
  • FlowDirection with Non-text Elements
  • Number Substitution

FlowDirection

The basic property that defines the content flow direction in a WPF application is FlowDirection. This property can be set to one of two enumeration values, LeftToRight or RightToLeft. The property is available to all WPF elements that inherit from FrameworkElement.

The following examples set the flow direction of a TextBox element.

Left-to-right flow direction

<TextBlock Background="DarkBlue" Foreground="LightBlue" 
   FontSize="20" FlowDirection="LeftToRight">
        This is a left-to-right TextBlock
</TextBlock>

Right-to-left flow direction

<TextBlock Background="LightBlue" Foreground="DarkBlue"
   FontSize="20" FlowDirection="RightToLeft">
        This is a right-to-left TextBlock
</TextBlock>

The following graphic shows how the previous code renders.

Graphic That Illustrates FlowDirection

Alinhamento de TextBlock

An element within a user interface (UI) tree will inherit the FlowDirection from its container. In the following example, the TextBlock is inside a Grid, which resides in a Window. Setting the FlowDirection for the Window implies setting it for the Grid and TextBlock as well.

The following example demonstrates setting FlowDirection.

<Window
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://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>

The top level Window has a RightToLeft FlowDirection, so all elements contained within it also inherit the same FlowDirection. For an element to override a specified FlowDirection it must add an explicit direction change such as the second TextBlock in the previous example which changes to LeftToRight. When no FlowDirection is defined, the default LeftToRight applies.

The following graphic shows the output of the previous example.

Graphic That Illustrates Explicitly Assigned FlowDirection

Ilustração da direção do fluxo

FlowDocument

Muitas plataformas de desenvolvimento, como HTML, Win32 e Java fornecem suporte especial para o desenvolvimento de conteúdo bidirecional. Linguagens de marcação, como HTML dar os criadores de conteúdo a marcação necessária para exibir texto em qualquer direção necessária, por exemplo o HTML 4.0 tag, "dir" que leva "rtl" ou "ltr" como valores. This tag is similar to the FlowDirection property, but the FlowDirection property works in a more advanced way to layout textual content and can be used for content other than text.

In WPF, a FlowDocument is a versatile UI element that can host a combination of text, tables, images and other elements. The samples in the following sections use this element.

Adding text to a FlowDocument can be done in more that one way. A simple way to do so is through a Paragraph which is a block-level element used to group content such as text. Para adicionar texto a elementos de nível incorporado a usar as amostras de Span e Run. Spané um elemento de conteúdo de fluxo de nível incorporado usado para agrupar os outros elementos inline, enquanto um Run é um fluxo de nível incorporado conteúdo de elemento deve conter uma seqüência de texto não formatado. A Span can contain multiple Run elements.

O primeiro exemplo de documento contém um documento que tenha um número de rede para compartilhar nomes; Por exemplo \\server1\folder\file.ext. Whether you have this network link in an Arabic or English document, you always want it to appear in the same way. The following graphic shows the link in an Arabic RightToLeft document.

Graphic That Illustrates Using the Span Element

Documento que flui da direita para a esquerda

Porque o texto é RightToLeft, todos os caracteres especiais, como, por exemplo, "\", separam o texto em um direito de ordem para a esquerda. That results in the link not being shown in the correct order, therefore to solve the problem, the text must be embedded to preserve a separate Run flowing LeftToRight. Instead of having a separate Run for each language, a better way to solve the problem is to embed the less frequently used English text into a larger Arabic Span.

The following graphic illustrates this.

Graphic That Illustrates Using the Run Element Embedded in a Span Element

Captura de tela de XamlPad

The following example demonstrates using Run and Span elements in documents.

<Page
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://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 Elements

The Span element works as a boundary separator between texts with different flow directions. Even Span elements with the same flow direction are considered to have different bidirectional scopes which means that the Span elements are ordered in the container’s FlowDirection, only the content within the Span element follows the FlowDirection of the Span.

The following graphic shows the flow direction of several TextBlock elements.

Graphic That Illustrates FlowDirection in Several TextBlock Elements

Blocos de texto com diferentes direções de fluxo

The following example shows how to use the Span and Run elements to produce the results shown in the previous graphic.

<Page xmlns="https://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>

In the TextBlock elements in the sample, the Span elements are laid out according to the FlowDirection of their parents, but the text within each Span element flows according to its own FlowDirection. This is applicable to Latin and Arabic – or any other language.

Adding xml:lang

O gráfico a seguir mostra outro exemplo que usa os números e expressões aritméticas, como "200.0+21.4=221.4". Notice that only the FlowDirection is set.

Graphic That Displays Numbers Using Only FlowDirection

Números que fluem da direita para a esquerda

Users of this application will be disappointed by the output, even though the FlowDirection is correct the numbers are not shaped as Arabic numbers should be shaped.

Elementos XAML podem incluir um XML atributo (xml:lang) que define o idioma de cada elemento. XAML também oferece suporte a uma XML o princípio de linguagem na qual xml:lang aplicados aos elementos do pai na árvore de valores são usados por todos os elementos filho. No exemplo anterior, porque um idioma não foi definido para o Run elemento ou qualquer um dos seus elementos de nível superior, o padrão xml:lang foi usado, que é en-US para XAML. O algoritmo de shaping número interno de Windows Presentation Foundation (WPF) seleciona os números no idioma correspondente – nesse caso inglês. Para tornar o árabe números render corretamente xml:lang precisa ser definido.

O gráfico a seguir mostra o exemplo com xml:lang adicionado.

Graphic That Illustrates Using the xml:lang Attribute

Números arábicos que fluem da direita para a esquerda

O exemplo a seguir adiciona xml:lang para o aplicativo.

<Page
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://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>

Lembre-se de que muitos idiomas têm diferentes xml:lang valores, dependendo da região de destino, por exemplo, "ar-SA" e "ar-EG" representam duas variações árabe. Os exemplos anteriores ilustram que você precise definir ambos os xml:lang e FlowDirection valores.

FlowDirection with Non-text Elements

FlowDirection defines not only how text flows in a textual element but also the flow direction of almost every other UI element. The following graphic shows a ToolBar that uses a horizontal LinearGradientBrush to draw its background.

Graphic That Shows a ToolBar with a Left to Right Gradient

Captura de tela de gradiente

After setting the FlowDirection to RightToLeft, not only the ToolBar buttons are arranged from right to left, but even the LinearGradientBrush realigns its offsets to flow from right to left.

The following graphic shows the realignment of the LinearGradientBrush.

Graphic That Shows a ToolBar with a Right to Left Gradient

Um gradiente que flui da direita para a esquerda

The following example draws a RightToLeft ToolBar. (To draw it left to right, remove the FlowDirection attribute on the ToolBar.

<Page
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="https://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 Exceptions

There are a few cases where FlowDirection does not behave as expected. This section covers two of these exceptions.

Image

An Image represents a control that displays an image. In XAML it can be used with a Source property that defines the uniform resource identifier (URI) of the Image to display.

Unlike other UI elements, an Image does not inherit the FlowDirection from the container. However, if the FlowDirection is set explicitly to RightToLeft, an Image is displayed flipped horizontally. This is implemented as a convenient feature for developers of bidirectional content; because in some cases, horizontally flipping the image produces the desired effect.

The following graphic shows a flipped Image.

Graphic That Illustrates a Flipped Image

Captura de tela de XamlPad

The following example demonstrates that the Image fails to inherit the FlowDirection from the StackPanel that contains it. Note   You must have a file named ms_logo.jpg on your C:\ drive to run this example.

<StackPanel 
  xmlns='https://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>

Note   Included in the download files is an ms_logo.jpg file. The code assumes that the .jpg file is not inside your project but somewhere on the C:\ drive. You must copy the .jpg from the project files to your C:\ drive or change the code to look for the file inside the project. To do this change Source="file://c:/ms_logo.jpg" to Source="ms_logo.jpg".

Paths

In addition to an Image, another interesting element is Path. A Path is an object that can draw a series of connected lines and curves. It behaves in a manner similar to an Image regarding its FlowDirection; for example its RightToLeft FlowDirection is a horizontal mirror of its LeftToRight one. However, unlike an Image, Path inherits its FlowDirection from the container and one does not need to specify it explicitly.

The following example draws a simple arrow using 3 lines. The first arrow inherits the RightToLeft flow direction from the StackPanel so that its start and end points are measured from a root on the right side. The second arrow which has an explicit RightToLeft FlowDirection also starts on the right side. However, the third arrow has its starting root on the left side. For more information on drawing see LineGeometry and GeometryGroup.

<StackPanel 
  xmlns='https://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>

The following graphic shows the output of the previous example.

Graphic That Illustrates Arrows Drawn Using the Path Element

Caminhos

The Image and Path are two examples of a how Windows Presentation Foundation (WPF) uses FlowDirection. Beside laying out UI elements in a specific direction within a container, FlowDirection can be used with elements such as InkPresenter which renders ink on a surface, LinearGradientBrush, RadialGradientBrush. Whenever you need a right to left behavior for your content that mimics a left to right behavior, or vice versa, Windows Presentation Foundation (WPF) provides that capability.

Number Substitution

Historically, Windows has supported number substitution by allowing the representation of different cultural shapes for the same digits while keeping the internal storage of these digits unified among different locales, for example numbers are stored in their well known hexadecimal values, 0x40, 0x41, but displayed according to the selected language.

This has allowed applications to process numerical values without the need to convert them from one language to another, for example a user can open an Microsoft Excel spreadsheet in a localized Arabic Windows and see the numbers shaped in Arabic, but open it in a European version of Windows and see European representation of the same numbers. This is also necessary for other symbols such as comma separators and percentage symbol because they usually accompany numbers in the same document.

Windows Presentation Foundation (WPF) continues the same tradition, and adds further support for this feature that allows more user control over when and how substitution is used. While this feature is designed for any language, it is particularly useful in bidirectional content where shaping digits for a specific language is usually a challenge for application developers because of the various cultures an application might run on.

The core property controlling how number substitution works in Windows Presentation Foundation (WPF) is the Substitution dependency property. The NumberSubstitution class specifies how numbers in text are to be displayed. It has three public properties that define its behavior. Following is a summary of each of the properties.

CultureSource:

This property specifies how the culture for numbers is determined. It takes one of three NumberCultureSource enumeration values.

CultureOverride:

The CultureOverride property is used only if the CultureSource property is set to Override and is ignored otherwise. It specifies the number culture. A value of null, the default value, is interpreted as en-US.

Substitution:

This property specifies the type of number substitution to perform. It takes one of the following NumberSubstitutionMethod enumeration values.

  • AsCulture: O método de substituição é determinado com base na cultura de número NumberFormatInfo.DigitSubstitution propriedade. This is the default.

  • Context: Se a cultura de número é uma cultura árabe ou Farsi, especifica que os dígitos dependem de contexto.

  • European: Números sempre são renderizados como dígitos europeus.

  • NativeNational: Números são processados usando os dígitos nacionais para a cultura de número, conforme especificado pela cultura NumberFormat.

  • Traditional: Números são processados usando os dígitos tradicionais para a cultura de número. Na maioria das culturas, isso é o mesmo que NativeNational. No entanto, NativeNational resulta em dígitos latino para algumas culturas árabe, enquanto este valor resulta em árabe dígitos para todas as culturas árabe.

What do those values mean for a bidirectional content developer? Na maioria dos casos, o desenvolvedor pode precisar somente definir FlowDirection e o idioma de cada textual UI elemento, por exemplo, Language="ar-SA" e o NumberSubstitution lógica se encarrega de exibir os números de acordo com a correta UI. The following example demonstrates using Arabic and English numbers in a Windows Presentation Foundation (WPF) application running in an Arabic version of Windows.

<Page 
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="https://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>

The following graphic shows the output of the previous sample if you are running in an Arabic version of Windows.

Graphic That Shows Arabic and English Numbers Displayed

Captura de tela de XamlPad com números

The FlowDirection was important in this case because setting the FlowDirection to LeftToRight instead would have yielded European digits. The following sections discuss how to have a unified display of digits throughout your document. If this example is not running on Arabic Windows, all the digits display as European digits.

Defining Substitution Rules

In a real application you might need to set the Language programmatically. Por exemplo, você deseja definir o xml:lang atributo para ser o mesmo usado pelo sistema de UI, ou talvez alterar o idioma de acordo com o estado do aplicativo.

If you want to make changes based on the application's state, make use of other features provided by Windows Presentation Foundation (WPF).

First, set the application component’s NumberSubstitution.CultureSource="Text". Usando essa configuração certifica-se de que as configurações não sejam provenientes de UI para os elementos de texto que têm "Usuário" como padrão, como TextBlock.

For example:

<TextBlock

Name="text1" NumberSubstitution.CultureSource="Text">

1234+5679=6913

</TextBlock>

No correspondente C# o código, defina a Language propriedade, por exemplo, para "ar-SA".

text1.Language =

System.Windows.Markup.XmlLanguage.GetLanguage("ar-SA");

Se você precisa definir a Language propriedade para o usuário atual UI usar a linguagem de código a seguir.

text1.Language =

System.Windows.Markup.XmlLanguage.GetLanguage(

System.Globalization.CultureInfo.CurrentUICulture.IetfLanguageTag);

CurrentCulture represents the current culture used by the current thread at run time.

Your final XAML example should be similar to the following example.

<Page x:Class="WindowsApplication.Window1"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://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>

Your final C# example should be similar to the following.

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;
            }
        }
    }
}

The following graphic shows what the window looks like for either programming language.

Graphic That Displays Arabic Numbers

Números arábicos

Using the Substitution Property

The way number substitution works in Windows Presentation Foundation (WPF) depends on both the Language of the text element and its FlowDirection. If the FlowDirection is left to right, then European digits are rendered. Entretanto se ele é precedido pelo texto em árabe ou tem o idioma é definido como "ar" e o FlowDirection é RightToLeft, árabe dígitos são processados em vez disso.

In some cases, however, you might want to create a unified application, for example European digits for all users. Or Arabic digits in Table cells with a specific Style. One easy way to do that is using the Substitution property.

In the following example, the first TextBlock does not have the Substitution property set, so the algorithm displays Arabic digits as expected. However in the second TextBlock, the substitution is set to European overriding the default substitution for Arabic numbers, and European digits are displayed.

<Page x:Class="WindowsApplication.Window1"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://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>