Información general sobre características bidireccionales en WPF

A diferencia de cualquier otra plataforma de desarrollo, WPF tiene muchas características que admiten el desarrollo rápido de contenido bidireccional; por ejemplo, datos de izquierda a derecha y de derecha a izquierda mezclados en el mismo documento. Al mismo tiempo, WPF crea una experiencia excelente para aquellos usuarios que requieren características bidireccionales, como son los usuarios de los idiomas árabe y hebreo.

En las secciones siguientes se explican muchas características bidireccionales, junto con ejemplos que muestran cómo conseguir la mejor presentación de contenido bidireccional. La mayoría de los ejemplos utiliza XAML, aunque puede aplicar con facilidad los mismos conceptos al código C# o Microsoft Visual Basic.

Este tema contiene las secciones siguientes.

  • FlowDirection
  • FlowDocument
  • Elementos Span
  • FlowDirection con elementos que no son de texto
  • Sustitución de números

FlowDirection

La propiedad básica que define la dirección de flujo del contenido en una aplicación de WPF es FlowDirection. Esta propiedad se puede establecer en uno de dos valores de enumeración, LeftToRight o RightToLeft. La propiedad está disponible para todos los elementos WPF que heredan de FrameworkElement.

Los ejemplos siguientes establecen la dirección de flujo de un elemento TextBox.

Dirección de flujo de izquierda a derecha

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

Dirección de flujo de derecha a izquierda

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

El gráfico siguiente muestra cómo se representa el código anterior.

Gráfico que ilustra el uso de FlowDirection

Alineación de TextBlock

Un elemento dentro de un árbol user interface (UI) heredará la propiedad FlowDirection de su contenedor. En el ejemplo siguiente, el objeto TextBlock está dentro de otro objeto Grid, que reside en Window. Si se establece la propiedad FlowDirection para Window, también se establece para Grid y TextBlock.

En el ejemplo siguiente se muestra cómo establecer 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>

El objeto de nivel superior Window tiene el valor RightToLeftFlowDirection, por lo que todos los elementos incluidos dentro de él también heredan la misma propiedad FlowDirection. Para que un elemento invalide una propiedad FlowDirection especificada, debe agregar un cambio de dirección explícito, como el segundo objeto TextBlock del ejemplo anterior, que cambia a LeftToRight. Cuando no se define ninguna propiedad FlowDirection, se aplica el valor LeftToRight predeterminado.

En el gráfico siguiente se muestra el resultado del ejemplo anterior.

Gráfico que ilustra el uso de la propiedad FlowDirection asignada explícitamente

Ilustración de dirección de flujo

FlowDocument

Muchas plataformas de desarrollo como HTML, Win32 y Java proporcionan una compatibilidad especial para el desarrollo de contenido bidireccional. Los lenguajes de marcado como HTML ofrecen a los creadores de contenido el marcado necesario para mostrar texto en cualquier dirección; por ejemplo, la etiqueta "dir" de HTML 4.0, que toma "rtl" o "ltr" como valores. Esta etiqueta es similar a la propiedad FlowDirection, pero la propiedad FlowDirection funciona de una manera más avanzada para crear el diseño del contenido textual y se puede utilizar para contenido que no sea texto.

En WPF, un objeto FlowDocument es un elemento de la UI versátil que puede hospedar una combinación de texto, tablas, imágenes y otros elementos. Los ejemplos de las secciones siguientes utilizan este elemento.

La adición de texto a FlowDocument se puede realizar de varias maneras. Una manera sencilla de hacerlo es mediante un objeto Paragraph, que es un elemento de nivel de bloque utilizado para agrupar contenido, por ejemplo texto. Para agregar texto a elementos insertados, los ejemplos utilizan Span y Run. Span es un elemento de contenido dinámico insertado utilizado para agrupar otros elementos insertados, mientras que un objeto Run es un elemento de contenido dinámico insertado pensado para contener una ejecución de texto sin formato. Un objeto Span puede contener varios elementos Run.

El primer ejemplo de documento contiene un documento con varios nombres de recursos compartidos de red; por ejemplo \\server1\folder\file.ext. Tanto si tiene este vínculo de red en un documento en árabe o en inglés, es conveniente que aparezca siempre de la misma manera. El gráfico siguiente muestra el vínculo en un documento RightToLeft en árabe.

Gráfico que ilustra cómo utilizar el elemento Span

Documento con flujo de texto de derecha a izquierda

Dado que el texto es RightToLeft, todos los caracteres especiales, como la "\", separan el texto de derecha a izquierda. Eso hace que el vínculo no se muestre en el orden correcto; por lo tanto, para resolver el problema, el texto se debe incrustar para conservar un objeto Run independiente que fluya LeftToRight. En lugar de tener un objeto Run independiente para cada idioma, una mejor manera de resolver el problema es incrustar el texto en inglés utilizado con menos frecuencia en un objeto Span de mayor tamaño en árabe.

Esto se ilustra en el siguiente gráfico:

Gráfico que muestra cómo utilizar el elemento Run incrustado en un elemento Span

Captura de pantalla de XamlPad

En el ejemplo siguiente se muestra cómo utilizar los elementos Run y Span en los documentos.

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

Elementos Span

El elemento Span funciona como un separador de límite entre textos con direcciones de flujo diferentes. Incluso los elementos Span con la misma dirección de flujo tienen ámbitos bidireccionales diferentes, lo que significa que los elementos Span se ordenan en el objeto FlowDirectiondel contenedor; sólo el contenido dentro de Span sigue la dirección determinada por FlowDirection para el elemento Span.

El gráfico siguiente muestra la dirección de flujo de varios elementos TextBlock.

Gráfico que ilustra FlowDirection en varios elementos TextBlock

Bloques de texto con diferentes direcciones de flujo

El ejemplo siguiente muestra cómo utilizar los elementos Span y Run para generar los resultados mostrados en el gráfico anterior.

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

En los elementos TextBlock del ejemplo, los elementos Span se disponen de acuerdo con la FlowDirection de sus elementos primarios, pero el texto dentro de cada elemento Span fluye según su propia FlowDirection. Esto es aplicable al latín y al árabe, o a cualquier otro idioma.

Agregar xml:lang

En el gráfico siguiente se muestra otro ejemplo que utiliza números y expresiones aritméticas, como "200.0+21.4=221.4". Observe que sólo se establece FlowDirection.

Gráfico que muestra números usando sólo FlowDirection

Números que fluyen de derecha a izquierda

El resultado defraudará a los usuarios de esta aplicación; aunque FlowDirection es correcto, los números no tienen la forma que deben tener los números árabes.

Los elementos XAML pueden incluir un atributo XML (xml:lang) que define el idioma de cada elemento. XAML también admite un principio del lenguaje XML en virtud del cual los valores de xml:lang aplicados a los elementos primarios de un árbol se utilizan en los elementos secundarios. En el ejemplo anterior, dado que no se definió un idioma para el elemento Run ni para ninguno de sus elementos de nivel superior, se utilizó el idioma predeterminado de xml:lang, que es en-US para XAML. El algoritmo interno de cambio de forma de números de Windows Presentation Foundation (WPF) selecciona los números en el idioma correspondiente, en este caso inglés. Para poder presentar correctamente los números árabes es necesario establecer xml:lang.

En la ilustración siguiente se muestra el ejemplo al que se ha agregado xml:lang.

Gráfico que ilustra cómo utilizar el atributo xml:lang

Números árabes que fluyen de derecha a izquierda

El ejemplo siguiente agrega xml:lang a la aplicación.

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

Tenga en cuenta que muchos idiomas tienen valores de xml:lang diferentes que dependen de la región concreta; por ejemplo, "ar-SA" y "ar-EG" representan dos variaciones del árabe. Los ejemplos anteriores muestran que es necesario definir los valores de xml:lang y de FlowDirection.

FlowDirection con elementos que no son de texto

FlowDirection define no sólo el flujo del texto en un elemento textual, sino también la dirección de flujo de casi todos los demás elementos de la UI. El gráfico siguiente muestra un elemento ToolBar que utiliza un LinearGradientBrush horizontal para dibujar su fondo.

Gráfico que muestra una barra de herramientas con un degradado de izquierda a derecha

Captura de pantalla de degradado

Después de establecer el elemento FlowDirection en RightToLeft, no sólo los botones de ToolBar están organizados de derecha a izquierda, sino que incluso LinearGradientBrush realinea sus desplazamientos para que fluyan de derecha a izquierda.

El gráfico siguiente muestra la realineación del elemento LinearGradientBrush.

Gráfico que muestra una barra de herramientas con un degradado de derecha a izquierda

Degradado que fluye de derecha a izquierda

El ejemplo siguiente dibuja un valor RightToLeft ToolBar. (Para dibujarlo de izquierda a derecha, quite el atributo FlowDirection de 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>

Excepciones de FlowDirection

Hay algunos casos en los que FlowDirection no se comporta de la forma esperada. En esta sección se describen dos de estas excepciones.

Image

Un elemento Image representa un control que muestra una imagen. En XAML, se puede utilizar con una propiedad Source que define el uniform resource identifier (URI) del elemento Image que se va a mostrar.

A diferencia de otros elementos de la UI, Image no hereda FlowDirection del contenedor. Sin embargo, si se establece explícitamente FlowDirection en RightToLeft, Image se muestra volteado horizontalmente. Esto se implementa como una característica útil para los programadores de contenido bidireccional, ya que, en algunos casos, al voltear horizontalmente la imagen se genera el efecto deseado.

El gráfico siguiente muestra un elemento Image volteado.

Gráfico que ilustra una imagen volteada

Captura de pantalla de XamlPad

En el ejemplo siguiente se muestra que Image no hereda el valor FlowDirection del StackPanel que lo contiene. Nota: debe tener un archivo denominado ms_logo.jpg en la unidad C:\ para poder ejecutar este ejemplo.

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

Nota: en los archivos de descarga se incluye un archivo ms_logo.jpg. El código presupone que el archivo .jpg no está en el proyecto sino en alguna parte de la unidad C:\. Debe copiar el .jpg de los archivos de proyecto a la unidad C:\ o cambiar el código para buscar el archivo en el proyecto. Para ello, cambie Source="file://c:/ms_logo.jpg" a Source="ms_logo.jpg".

Path

Además de un objeto Image, otro elemento interesante es Path. Un trazado es un objeto que puede dibujar una serie de líneas y curvas conectadas. Se comporta de una manera similar a un elemento Image en relación con su FlowDirection; por ejemplo su FlowDirection RightToLeft es una imagen reflejada horizontal de su LeftToRight. Sin embargo, a diferencia de Image, Path hereda su valor FlowDirection del contenedor y no es necesario especificarlo explícitamente.

El ejemplo siguiente dibuja una flecha simple mediante 3 líneas. La primera flecha hereda la dirección de flujo RightToLeft del elemento StackPanel para que sus puntos inicial y final se midan desde una raíz en el lado derecho. La segunda flecha, que tiene un valor RightToLeft explícito para FlowDirection, también se inicia en el lado derecho. Sin embargo, la tercera flecha tiene su raíz de inicio en el lado izquierdo. Para obtener más información sobre cómo dibujar, vea LineGeometry y 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>

En el gráfico siguiente se muestra el resultado del ejemplo anterior.

Gráfico que ilustra las flechas dibujadas mediante el elemento Path

Trayectos

Los elementos Image y Path son dos ejemplos de cómo Windows Presentation Foundation (WPF) utiliza FlowDirection. Además de disponer los elementos de la UI en una dirección concreta dentro de un contenedor, FlowDirection se puede utilizar con elementos como InkPresenter que representa la entrada de lápiz en una superficie, LinearGradientBrush y RadialGradientBrush. Siempre que necesite un comportamiento de derecha a izquierda para el contenido que imite un comportamiento de izquierda a derecha, o viceversa, Windows Presentation Foundation (WPF) proporciona esa función.

Sustitución de números

Tradicionalmente, Windows ha admitido la sustitución de números permitiendo la representación de formas culturales diferentes para los mismos dígitos, manteniendo al mismo tiempo unificado el almacenamiento interno de estos dígitos entre las distintas configuraciones regionales; por ejemplo, los números se almacenan en sus valores hexadecimales ya conocidos, 0x40, 0x41, pero se muestran según el idioma seleccionado.

Esto ha permitido a las aplicaciones procesar los valores numéricos sin necesidad de convertirlos de un idioma a otro; por ejemplo, un usuario puede abrir una hoja de cálculo de Microsoft Excel en un sistema Windows localizado al árabe y ver los números en forma árabe, pero también puede abrirlo en una versión europea de Windows y ver la representación europea de los mismos números. Esto también es necesario para otros símbolos, como los separadores de coma y el símbolo del porcentaje, ya que éstos normalmente acompañan a los números en el mismo documento.

Windows Presentation Foundation (WPF) continúa con la misma tradición y agrega una compatibilidad más extensa para esta característica, permitiendo un mayor control del usuario sobre cuándo y cómo se utiliza la sustitución. Aunque esta característica está diseñada para cualquier idioma, es particularmente útil en el contenido bidireccional, donde dar forma a los dígitos para un idioma concreto normalmente constituye un desafío para los desarrolladores de aplicaciones debido a las distintas referencias culturales en las que se podría ejecutar una aplicación.

La propiedad básica que controla cómo funciona la sustitución de números en Windows Presentation Foundation (WPF) es la propiedad de dependencia Substitution. La clase NumberSubstitution especifica cómo se muestran los números en el texto. Dicha clase tiene tres propiedades públicas que definen su comportamiento. A continuación se muestra un resumen de cada una de las propiedades.

CultureSource:

Esta propiedad especifica cómo se determina la referencia cultural para los números. Puede tomar uno de los tres valores de enumeración de NumberCultureSource siguientes.

CultureOverride:

La propiedad CultureOverride se utiliza sólo si la propiedad CultureSource se establece en Override; de lo contrario, se omite. Especifica la referencia cultural de número. El valor null, el valor predeterminado, se interpreta como en-US.

Substitution:

Esta propiedad especifica el tipo de sustitución de números que se va a realizar. Puede tomar uno de los valores de enumeración NumberSubstitutionMethod siguientes.

  • AsCulture: el método de sustitución se determina basándose en la propiedad NumberFormatInfo.DigitSubstitution de la referencia cultural de número. Éste es el valor predeterminado.

  • Context: si la referencia cultural de número es árabe o persa, especifica que los dígitos dependen del contexto.

  • European: los números siempre se presentan como dígitos europeos.

  • NativeNational: los números se presentan utilizando los dígitos nacionales para la referencia cultural de número, tal y como lo especifica la propiedad NumberFormat de la referencia cultural.

  • Traditional: los números se presentan mediante dígitos tradicionales para la referencia cultural de número. Para la mayoría de las referencias culturales, esto es lo mismo que NativeNational. Sin embargo, NativeNational genera dígitos latinos en algunas referencias culturales árabes, mientras que este valor genera dígitos árabes para todas las referencias culturales árabes.

¿Qué significan esos valores para un programador de contenido bidireccional? En la mayoría de los casos, el desarrollador sólo necesitará definir FlowDirection y el idioma de cada elemento textual de la UI, por ejemplo Language="ar-SA"; la lógica de NumberSubstitution se encargará de mostrar los números según la UI correcta. El ejemplo siguiente muestra cómo utilizar números árabes e ingleses en una aplicación de Windows Presentation Foundation (WPF) que se ejecuta en una versión de Windows en árabe.

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

El gráfico siguiente muestra el resultado del ejemplo anterior si se está ejecutando una versión de Windows en árabe.

Gráfico que muestra los números árabes e ingleses presentados

Captura de pantalla de XamlPad con números

El elemento FlowDirection era importante en este caso porque si se hubiese establecido FlowDirection en LeftToRight, se habrían generado dígitos europeos. Las secciones siguientes describen cómo conseguir una presentación unificada de dígitos en todo el documento. Si este ejemplo no se ejecuta en un sistema de Windows en árabe, todos los dígitos se mostrarán como dígitos europeos.

Definir las reglas de sustitución

En una aplicación real es posible que necesite establecer el idioma mediante programación. Por ejemplo, imagine que desea establecer el atributo xml:lang para que sea igual que el utilizado por la UI del sistema, o cambiar el idioma dependiendo del estado de la aplicación.

Si desea realizar modificaciones basándose en el estado de la aplicación, utilice otras características de Windows Presentation Foundation (WPF).

En primer lugar, establezca el valor NumberSubstitution.CultureSource="Text" del componente de aplicación. Utilice dicho valor para asegurarse de que la configuración no procede de la UI para los elementos de texto que tienen "User" como valor predeterminado, como por ejemplo TextBlock.

Por ejemplo:

<TextBlock

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

1234+5679=6913

</TextBlock>

En el código C# correspondiente, establezca la propiedad Language por ejemplo en "ar-SA".

text1.Language =

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

Si necesita establecer la propiedad Language en el idioma de la UI del usuario actual, utilice el código siguiente.

text1.Language =

System.Windows.Markup.XmlLanguage.GetLanguage(

System.Globalization.CultureInfo.CurrentUICulture.IetfLanguageTag);

CurrentCulture representa la referencia cultural actual utilizada por el subproceso actual en tiempo de ejecución.

El último ejemplo de XAML debería ser similar al siguiente.

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

El último ejemplo de C# debería ser similar al siguiente.

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

El gráfico siguiente muestra la apariencia de la ventana para cualquiera de los dos lenguajes de programación.

Gráfico que muestra números árabes

Números árabes

Usar la propiedad de sustitución

El funcionamiento de la sustitución de números en Windows Presentation Foundation (WPF) depende tanto del idioma del elemento de texto como de su FlowDirection. Si el elemento FlowDirection es de izquierda a derecha, se representan dígitos europeos. Sin embargo, si va precedido de texto árabe o se ha establecido el idioma en "ar" y FlowDirection es RightToLeft, se presentan dígitos árabes.

En algunos casos, sin embargo, es conveniente crear una aplicación unificada, por ejemplo dígitos europeos para todos los usuarios. O dígitos árabes en celdas de un elemento Table con un Style concreto. Una forma fácil de hacerlo consiste en utilizar la propiedad Substitution.

En el ejemplo siguiente, el primer TextBlock no tiene establecida la propiedad Substitution, por lo que el algoritmo muestra dígitos árabes conforme a lo esperado. Sin embargo, en el segundo elemento TextBlock, la sustitución se ha establecido en europeo, lo que reemplaza la sustitución predeterminada para los números árabes, y hace que se muestren los dígitos europeos.

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