Etiqueta: C#Sharp

Temas relacionados con C#

  • Google Cloud Natural Language, ejemplo en Visual Basic .NET

    Pues eso… aquí te dejo un ejemplo para usar las API de Google Cloud Natural Language, pero para Visual Basic .NET

    Con esas API podrás analizar textos (también en español) y ver las palabras que la forman (tokens), su estructura sintáctica, etc.

    Nota:
    Para usar este código tendrás que crearte una cuenta en Google Cloud, generar una «key» para usarla y poco más, todos los pasos están explicados en este enlace (el código de ejemplo es para C#, pero te servirá.

    Pasos para crear un proyecto usando dotnet (cli):

    – Abre una ventana de consola (o terminal)
    – Posicionarse en la carpeta donde crear el proyecto
    – Crear el proyecto
    dotnet new console -n <nombre-proyecto>
    dotnet new console -lang VB -n <nombre-proyecto>
    – Cambiar al directorio del proyecto
    cd <nombre-proyecto>
    – Añadir el paquete de Google Cloud Natural Language API
    dotnet add package Google.Cloud.Language.V1
    – Copiar el fichero key.json con las claves y permisos
    – Ver estos pasos para crearla:
    https://codelabs.developers.google.com/codelabs/cloud-natural-language-csharp#3
    – En IAM, añadir la cuenta creada (incluida en el fichero key.json) en +OTORGAR ACCESO
    Solo estará la principal y/o las otras añadidas
    – Si se ha usado otra cuenta, estará en IAM>Cuentas de Servicio
    – Modificar Program.cs (o Program.vb) para usar el código que accede a la API de Natural Language
    – Ejecutar el código
    dotnet run

    Notas:
    – Debes crear una variable de entorno en Windows, (lo puedes hacer desde la misma consola) indicando el path donde estará el fichero key.json.
    – Lo que yo hago es copiar ese fichero en la carpeta del ejecutable y la variable de entorno la defino de esta forma:
    set GOOGLE_APPLICATION_CREDENTIALS=key.json
    – Puedes modificar el fichero del proyecto y añadir lo siguiente:

      <ItemGroup>
        <None Update="key.json">
          <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </None>
      </ItemGroup>
    

    El código de ejemplo

    '--------------------------------------------------------------------------------
    ' Ejemplo de Google Cloud Natural Language en Visual Basic .NET (31/ene/23 18.50)
    '
    ' (c)Guillermo Som (Guille), 2023
    '--------------------------------------------------------------------------------
    
    Imports System
    Imports System.Text
    Imports gcl = Google.Cloud.Language.V1
    Imports Google.Protobuf.Collections
    Imports Google.Cloud.Language.V1.AnnotateTextRequest.Types
    
    'Namespace NaturalLanguageApiDemo
    Class Program
        Shared client As gcl.LanguageServiceClient '?
    
        Shared Sub Main(args As String())
            'Dim text = "El 8 de Febrero voy en bici al Camino de Santiago desde Sarria ¿crees que aguantaré?"
            Dim text = "Probando Google Cloud Natural Language con VB.NET ¿Funcionará esto?"
    
            Console.WriteLine("Ejemplos de Google.Cloud.Language")
            Console.WriteLine()
            Console.WriteLine("Pruebas de Google Cloud Natural Language en Visual Basic .NET")
            Console.WriteLine()
            Console.WriteLine("  Creando el cliente...")
            client = gcl.LanguageServiceClient.Create()
            Console.WriteLine()
    
            Dim repitiendo As Boolean = False
    
            Do
    
                If repitiendo Then
                    Console.WriteLine($"Última: '{text}'")
                    Console.WriteLine("Indica la frase que quieres analizar (0 salir, [la última])")
                Else
                    Console.WriteLine($"Predeterminada: '{text}'")
                    Console.WriteLine("Indica la frase que quieres analizar (0 salir, [predeterminada])")
                End If
    
                Console.Write("> ")
                Dim resText = Console.ReadLine()
    
                If Not String.IsNullOrEmpty(resText) Then
    
                    If resText = "0" Then
                        Exit Do
                    End If
    
                    text = resText
                End If
    
                Do
                    Console.WriteLine($"Analizar: '{text}'")
                    Console.Write("1- Todo con tokens, 2- Todo sin tokens, 3- Solo tokens, 0- nueva frase [2] ? ")
                    resText = Console.ReadLine()
                    Console.WriteLine()
    
                    If String.IsNullOrEmpty(resText) Then
                        resText = "2"
                    End If
    
                    If resText = "1" Then
                        Analizar(text, conTokens:=True)
                    ElseIf resText = "2" Then
                        Analizar(text, conTokens:=False)
                    ElseIf resText = "3" Then
                        AnalizarTokens(text)
                    ElseIf resText = "0" Then
                        Exit Do
                    End If
    
                    Console.WriteLine()
                Loop While True
    
                repitiendo = True
            Loop While True
        End Sub
    
        Private Shared Sub Analizar(text As String, conTokens As Boolean)
            If client Is Nothing Then
                client = gcl.LanguageServiceClient.Create()
            End If
    
            Dim document = gcl.Document.FromPlainText(text)
            Dim response As gcl.AnnotateTextResponse
    
            Try
                response = client.AnnotateText(document, New Features With {
                        .ExtractSyntax = True,
                        .ExtractEntities = True,
                        .ExtractDocumentSentiment = True,
                        .ExtractEntitySentiment = True,
                        .ClassifyText = True
                    })
            Catch
                response = client.AnnotateText(document, New Features With {
                        .ExtractSyntax = True,
                        .ExtractEntities = True,
                        .ExtractDocumentSentiment = True,
                        .ExtractEntitySentiment = True
                    })
            End Try
    
            Dim sentiment = response.DocumentSentiment
            Console.WriteLine($"Detected language: {response.Language}")
            Console.WriteLine($"Sentiment Score: {sentiment.Score}, Magnitude: {sentiment.Magnitude}")
            Console.WriteLine("***Entities:")
            Dim entity1 As gcl.Entity = Nothing
    
            For Each entity0 In response.Entities
    
                If entity1 Is Nothing Then
                    entity1 = entity0
                Else
                    If entity0.Equals(entity1) Then Continue For
                End If
    
                Console.WriteLine($"Entity: '{entity0.Name}'")
                Console.WriteLine($"  Type: {entity0.Type},  Salience: {CInt((entity0.Salience * 100))}%")
    
                If entity0.Mentions.Count > 0 Then
                    Console.WriteLine($"  Mentions: {entity0.Mentions.Count}")
    
                    For Each mention In entity0.Mentions
                        Console.Write($"    Text: '{mention.Text.Content}' (beginOffset: {mention.Text.BeginOffset}),")
                        Console.WriteLine($" Type: {mention.Type}, Sentiment: {mention.Sentiment}")
                    Next
                End If
    
                If entity0.Metadata.Count > 0 Then
                    Console.WriteLine($"  Metadata: {entity0.Metadata}")
    
                    If entity0.Metadata.ContainsKey("wikipedia_url") Then
                        Console.WriteLine($"    URL: {entity0.Metadata("wikipedia_url")}")
                    End If
                End If
            Next
    
            Console.WriteLine("***Categories:")
    
            For Each cat In response.Categories
                Console.WriteLine($"Category: '{cat.Name}' (Confidence: {cat.Confidence})")
            Next
    
            Console.WriteLine("***Sentences:")
    
            For Each sentence In response.Sentences
                Console.WriteLine($" Sentence.Text.Content: '{sentence.Text.Content}'")
                Console.WriteLine($"   Sentence.Text.BeginOffset: {sentence.Text.BeginOffset}")
                Console.WriteLine($" Sentence.Sentiment .Magnitude: {sentence.Sentiment.Magnitude}, .Score: {sentence.Sentiment.Score}")
            Next
    
            If conTokens Then
                Console.WriteLine("***Tokens:")
    
                For i As Integer = 0 To response.Tokens.Count - 1
                    MostrarToken(i, response.Tokens, conContenido:=False)
                Next
            End If
        End Sub
    
        Private Shared Sub AnalizarTokens(text As String)
            If client Is Nothing Then
                client = gcl.LanguageServiceClient.Create()
            End If
    
            Dim document = gcl.Document.FromPlainText(text)
            Dim response As gcl.AnnotateTextResponse
    
            Try
                response = client.AnnotateText(document, New Features With {
                        .ExtractSyntax = True,
                        .ExtractEntities = True,
                        .ExtractDocumentSentiment = True,
                        .ExtractEntitySentiment = True,
                        .ClassifyText = True
                    })
            Catch
                response = client.AnnotateText(document, New Features With {
                        .ExtractSyntax = True,
                        .ExtractEntities = True,
                        .ExtractDocumentSentiment = True,
                        .ExtractEntitySentiment = True
                    })
            End Try
    
            AnalizarSentecias(response)
        End Sub
    
        Private Shared Sub AnalizarSentecias(self As gcl.AnnotateTextResponse)
            Dim index As Integer = 0
    
            For Each sentence In self.Sentences
                Dim content = sentence.Text.Content
                Dim sentence_begin = sentence.Text.BeginOffset
                Dim sentence_end = sentence_begin + content.Length - 1
    
                While index < self.Tokens.Count AndAlso self.Tokens(index).Text.BeginOffset <= sentence_end
                    MostrarToken(index, self.Tokens, conContenido:=True)
                    index += 1
                End While
            Next
        End Sub
    
        Private Shared Sub MostrarToken(nToken As Integer, tokens As RepeatedField(Of gcl.Token), Optional conContenido As Boolean = True)
            Dim token As gcl.Token = tokens(nToken)
            Console.WriteLine($"{nToken}- Token: Text.Content: '{token.Text.Content}', Lemma: '{token.Lemma}'")
    
            If token.DependencyEdge.Label = gcl.DependencyEdge.Types.Label.Root Then
                Console.Write($"  **DependencyEdge Label: {token.DependencyEdge.Label}")
    
                If token.DependencyEdge.HeadTokenIndex <> nToken Then
                    Console.Write($", HeadTokenIndex: {token.DependencyEdge.HeadTokenIndex}")
                End If
    
                Console.WriteLine("**")
            Else
                Console.Write($"  DependencyEdge Label: {token.DependencyEdge.Label}, HeadTokenIndex: {token.DependencyEdge.HeadTokenIndex}")
                Dim tokenDependency = tokens(token.DependencyEdge.HeadTokenIndex)
                Console.WriteLine($" ('{tokenDependency.Text.Content}')")
            End If
    
            If conContenido Then
                Console.WriteLine($"  PartOfSpeech:")
                Console.Write($"    Tag: {token.PartOfSpeech.Tag},")
                Dim sb = New StringBuilder()
    
                If token.PartOfSpeech.Aspect <> gcl.PartOfSpeech.Types.Aspect.Unknown Then
                    sb.Append($" (Aspect: {token.PartOfSpeech.Aspect},")
    
                    If token.PartOfSpeech.[Case] <> gcl.PartOfSpeech.Types.[Case].Unknown Then
                        sb.Append($" Case: {token.PartOfSpeech.[Case]},")
                    End If
    
                    If token.PartOfSpeech.Form <> gcl.PartOfSpeech.Types.Form.Unknown Then
                        sb.Append($" Form: {token.PartOfSpeech.Form},")
                    End If
    
                    If sb.ToString().EndsWith(","c) Then
                        sb.Length -= 1
                    End If
    
                    sb.Append("),")
                End If
    
                If token.PartOfSpeech.Gender <> gcl.PartOfSpeech.Types.Gender.Unknown Then
                    sb.Append($" (Gender: {token.PartOfSpeech.Gender},")
    
                    If token.PartOfSpeech.Mood <> gcl.PartOfSpeech.Types.Mood.Unknown Then
                        sb.Append($" Mood: {token.PartOfSpeech.Mood},")
                    End If
    
                    If token.PartOfSpeech.Number <> gcl.PartOfSpeech.Types.Number.Unknown Then
                        sb.Append($" Number: {token.PartOfSpeech.Number},")
                    End If
    
                    If sb.ToString().EndsWith(","c) Then
                        sb.Length -= 1
                    End If
    
                    sb.Append("),")
                End If
    
                If token.PartOfSpeech.Proper <> gcl.PartOfSpeech.Types.Proper.Unknown Then
                    sb.Append($" Proper: {token.PartOfSpeech.Proper}")
                End If
    
                If sb.ToString().Trim().Length > 0 Then
                    Console.WriteLine(sb.ToString().TrimEnd(","c))
                End If
    
                sb.Clear()
                sb.Append("   ")
    
                If token.PartOfSpeech.Person <> gcl.PartOfSpeech.Types.Person.Unknown Then
                    sb.Append($" Person: {token.PartOfSpeech.Person},")
                End If
    
                If token.PartOfSpeech.Reciprocity <> gcl.PartOfSpeech.Types.Reciprocity.Unknown Then
                    sb.Append($" Reciprocity: {token.PartOfSpeech.Reciprocity},")
                End If
    
                If token.PartOfSpeech.Tense <> gcl.PartOfSpeech.Types.Tense.Unknown Then
                    sb.Append($" Tense: {token.PartOfSpeech.Tense},")
                End If
    
                If token.PartOfSpeech.Voice <> gcl.PartOfSpeech.Types.Voice.Unknown Then
                    sb.Append($" Voice: {token.PartOfSpeech.Voice}")
                End If
    
                If sb.ToString().Trim().Length > 0 Then
                    Console.WriteLine(sb.ToString())
                End If
            Else
                Console.WriteLine($"  PartOfSpeech Aspect: {token.PartOfSpeech.Aspect}, Case: {token.PartOfSpeech.[Case]}, Form: {token.PartOfSpeech.Form}")
                Console.WriteLine($"  PartOfSpeech Gender: {token.PartOfSpeech.Gender}, Mood: {token.PartOfSpeech.Mood}, Number: {token.PartOfSpeech.Number}")
                Console.WriteLine($"  PartOfSpeech Person: {token.PartOfSpeech.Person}, Proper: {token.PartOfSpeech.Proper}")
                Console.WriteLine($"  PartOfSpeech Reciprocity: {token.PartOfSpeech.Reciprocity}, Tag: {token.PartOfSpeech.Tag}")
                Console.WriteLine($"  PartOfSpeech Tense:: {token.PartOfSpeech.Tense}, Voice: {token.PartOfSpeech.Voice}")
            End If
        End Sub
    End Class
    'End Namespace

    Una captura

    La aplicación en funcionamiento

    Código fuente

    El código fuente del ejemplo para Visual Basic .NET, así como el de C#, los puedes ver/descargar desde este repositorio en GitHub.

    Y esto ha sido todo amigos… 😉

    Nos vemos.
    Guillermo

  • Evaluar expresiones (código en Java y C#)

    Pues eso… Sí, has leído bien el título: en Java. Y no en JavaScript (aparte de en C#).

    Y es que hace unos días, empecé a estudiar esto de Java a raíz que mi hijo David está haciendo un curso de Java (back-end) y era por si le echaba una mano en algunos conceptos, ya que lo que él estudio en su día fue COBOL, algo de Pascal y algo de Visual Basic 6, y claro… los conceptos de POO y esas cosas, como que no las tiene claras.

    Y para poder ayudarle (si es que así me lo hace saber), me puse a «trastear» en Java, y de camino he aprendido entre otras cosas, que hay diferencias, algunos grandes, en comparación con C# que es lo más parecido, (con Visual Basic ni te cuento).

    Algunas de esas diferencias son a favor de Java, y la mayoría, a favor de C# o si lo prefieres a favor de .NET, seguramente porque lo conozco más.

    Pero sea como sea, y peleándome un poco con el código y el IDE, que por cierto estoy usando uno que me parece muy bueno y es gratis: IntelliJ IDEA (Community Edition) de JetBrains, he conseguido hacer algo más o menos operativo y que me está enseñando algunas de esas cosas que ni siquiera sabía que C# las tenía y otras que al parecer C# tiene porque Java las tenía.

    Lo que más he echado en falta son las «tuplas», aunque en este código de evaluar expresiones aritméticas he solventado con records, más que nada porque en algunas funciones necesitaba devolver dos valores (seguramente en Java habrá otra forma de hacerlo, pero no la he encontrado). Otra cosa que he echado en falta en Java es las estructuras (tipos de datos definibles, pero por valor).

     

    No te voy a explicar «paso a paso» el código ni nada de eso (al menos por ahora), simplemente te voy a decir lo que este evaluador sabe hacer (creo que lo hace bien) y el código lo podrás ver en el repositorio de GitHub.

    Quiero comentarte que primero hice una versión usando records para hacer las operaciones de suma, restas, multiplicación y división (basándome en un ejemplo de la documentación de Java), y el último es usando tipos double que además de esas cuatro operaciones hace un par de ellas más: módulo y factorial.

    El enlace de GitHub o, mejor dicho, los enlaces son para el código basado en el tipo double, en esos repositorios tienes los enlaces al código fuente que usa los records, tanto para Java como para C#.

     

    Lo que hace el evaluador de expresiones

    • Evalúa expresiones entre paréntesis (con varios niveles de anidación).
    • Evalúa primero los operadores multiplicativos (* y x para multiplicar, / y : para dividir y % para el módulo) y después los operadores aditivos (+ para sumar, – para restar).
    • La expresión puede tener espacios, pero al evaluarla se quitan, por tanto: «1 5 * 2» se convierte en «15*2».
    • Si hay un paréntesis de apertura precedido por un dígito o de un paréntesis de cierre, se considera una multiplicación y se pone el signo *.
    • Si hay un paréntesis de cierre seguido de un dígito o de un paréntesis de apertura, se considera una multiplicación y se pone el signo *.
    • Evalúa factoriales (usando el carácter «!»), tanto de números enteros positivos (naturales) como números negativos con parte decimal.
    • El valor de la factorial para números no naturales se calcula usando la función gamma basada en un código adaptado de un ejemplo de StackOverflow.

     

    El código de Java y C# en GitHub

    Estos son los enlaces del código fuente que tengo publicado en GitHub:

    EvaluarExpresiones-java y EvaluarExpresiones-csharp.

    Espero que te sea de utilidad.

    Nos vemos.
    Guillermo

  • Trucos para .NET MAUI (segunda parte)

    Pues eso… seguimos con los trucos para .NET MAUI, en la primera parte te mostré cómo configurar el proyecto para usar las plataformas que prefieras y cómo configurar el aprovisionamiento para iOS (necesitas una cuenta de Apple Developer). Ahora vamos a ver algunas cosillas referentes al diseño de la aplicación.

    Para poder mostrarte estos trucos, he creado una aplicación para .NET MAUI con Visual Studio 2022 (community), pero no la versión Preview, ya que a la hora de escribir esto, la tengo desinstalada y así uso el .NET 6.0 (que es el que por ahora me está dando menos problemas, al menos teniendo el .NET 6 y el .NET 7 RC1).
    La versión de Visual Studio 2022 es:
    Microsoft Visual Studio Community 2022 (64-bit) Version 17.3.5

    Acabo de instalar la versión 17.3.6 y sigue funcionando bien 😉

    Truco 4: Mostrar bien los Frame sin que se corten

    Cuando añades un Frame se suelen cortar los bordes (ver la figura 1)

    Figura 1. Los frame se cortan las líneas

    El código XAML para mostrar esto es el siguiente:

    <ScrollView>
        <VerticalStackLayout
            Padding="10,0"
            VerticalOptions="Center">
    
            <Frame>
                <VerticalStackLayout Spacing="25">
                        <Image
                                Source="dotnet_bot.png"
                                SemanticProperties.Description="Cute dot net bot waving hi to you!"
                                HeightRequest="200"
                                HorizontalOptions="Center" />
    
                        <Label
                                Text="Hello, World!"
                                SemanticProperties.HeadingLevel="Level1"
                                FontSize="32"
                                HorizontalOptions="Center" />
    
                        <Label
                                Text="Welcome to .NET Multi-platform App UI"
                                SemanticProperties.HeadingLevel="Level2"
                                SemanticProperties.Description="Welcome to dot net Multi platform App U I"
                                FontSize="18"
                                HorizontalOptions="Center" />
    
                        <Button
                                x:Name="CounterBtn"
                                Text="Click me"
                                SemanticProperties.Hint="Counts the number of times you click"
                                Clicked="OnCounterClicked"
                                HorizontalOptions="Center" />
    
                </VerticalStackLayout>
            </Frame>
        </VerticalStackLayout>
    </ScrollView>

    Lo deseable es que esté como en la figura 2.

    Figura 2. Los Frame deben mostrar todos los bordes

    El truco consiste en añadir un margen al StackLayout que esté contenido en el Frame.

    <Frame>
        <VerticalStackLayout Spacing="25" Margin="4">
    

    ¿Fácil verdad?

    Nota:
    Este fallo solo ocurre en las aplicaciones de Windows (WinUI)

    Tal como te acabo de decir, este fallo (de que se corten las líneas del Frame) solo ocurre en las aplicaciones para Windows (WinUI), al menos en iOS y Android no pasa, tal como puedes ver en las capturas 3 y 4.

    Figura 3. La app de prueba en un iPhone 7 plus (iOS)
    Figura 4. La app funcionando en un Pixel 4a (Android)

    En realidad, al menos en iOS y Android, da igual que esté lo de Margin = «4» como que no, el efecto es prácticamente el mismo.

    Truco 5: Cambiar el tamaño de la ventana en Windows

    Otro de los problemas (al menos con la compilación actual de .NET MAUI) es que la ventana de la aplicación de Windows (WinUI) se muestra prácticamente a pantalla completa, y, ya te digo que al menos por ahora, ese tamaño no es configurable de forma automática o, que ocurra como en las aplicaciones para Xamarin en la que el usuario es el que decide qué tamaño y posición debe tener la ventana, de forma que en las próximas veces que se abra la aplicación se muestre como se dejó la última vez.

    Nota:
    En esta «issue de MAUI» dicen que se podrá hacer: Desktop: set window size and position #771 (pero ni idea de cómo o cuando estará disponible).

    Lo que yo hago en estos casos es usar un tamaño «fijo» (que no es lo suyo, pero…) y asignarlo de esta forma en el constructor de la clase App.

    public App()
        {
             InitializeComponent();
    
            // Indicar el tamaño para la app de Windows.
            Microsoft.Maui.Handlers.WindowHandler.Mapper.AppendToMapping(nameof(IWindow), (handler, view) =>
            {
    #if WINDOWS
    
                    // Asignar manualmente el tamaño. 
                    int winWidth = 1000;
                    int winHeight = 900;
    
                    var mauiWindow = handler.VirtualView;
                    var nativeWindow = handler.PlatformView;
                    nativeWindow.Activate();
                    IntPtr windowHandle = WinRT.Interop.WindowNative.GetWindowHandle(nativeWindow);
                    var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(windowHandle);
                    var appWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId);
                    appWindow.Resize(new Windows.Graphics.SizeInt32(winWidth, winHeight));
    
    #endif
            });
    
            MainPage = new AppShell();
        }
    

    Truco 6: Mostrar el título en la barra de la ventana (con colores personalizados)

    Otra cosa que estoy haciendo desde hoy (al probar en la aplicación gsCrearTablas_MAUI) es posicionando la ventana y de paso cambiando el color a la barra de título (para que no se vea el color ese tan feo) y también mostrando el título en esa barra de la ventana.

    Como puedes ver en la figura 2, no se muestra de color «normal» la barra de título y tampoco tiene un texto.

    En la captura 5 puedes ver el color y el texto en la barra de título que podrás conseguir con el código que te muestro a continuación (después de la captura).

    Figura 5. La app de Windows con texto y color en la barra de título

    La asignación del color y texto de la ventana lo conseguimos haciendo esta asignación (en el código mostrado antes) lo tendrías que poner al final, después de appWindow.Resize.

    // El título hay que asignarlo antes de asignar los colores.
    appWindow.Title = "Trucos MAUI by elGuille";
    // Este es el color que tiene en mi equipo la barra de título.
    appWindow.TitleBar.BackgroundColor = Microsoft.UI.ColorHelper.FromArgb(255, 0, 120, 212);
    appWindow.TitleBar.ForegroundColor = Microsoft.UI.Colors.White;
    

    Para poder posicionarla, hace falta un truquillo más que es que la ventana se haya mostrado, ya que, si queremos acceder al tamaño de la pantalla, nos dará un valor nulo (o cero).

    Nota:
    El color que asigno a la propiedad BackgroundColor lo he sacado de cómo se muestra el color en mi equipo, por tanto, en tu caso, lo mismo lo tienes que cambiar: FromArgb(255, 0, 120, 212).

    Truco 7: Esperar a que la ventana está mostrada para manipular la posición

    Esto lo pones también en el primer código que te mostré en el truco 5, después de appWindow.Resize. Fíjate que el cambio del color y el título hay que hacerlo dentro del Dispatcher.Dispatch, si no, el título no se muestra.

    // get screen size
    DisplayInfo disp = DeviceDisplay.Current.MainDisplayInfo;
    double x, y;
    
    // dispatcher is used to give the window time to actually resize
    Dispatcher.Dispatch(() =>
    {
        disp = DeviceDisplay.Current.MainDisplayInfo;
        x = (disp.Width / disp.Density - winWidth) / 2;
        if (x < 0) 
        {
            x = 0;
        }
        y = (disp.Height / disp.Density - winHeight) / 2;
        if (y < 0)
        {
            y = 0;
        }
        appWindow.Move(new Windows.Graphics.PointInt32((int)x, (int)y));
    
        // Si cambiamos la posición, esto hay que hacerlo en el Dispatcher.Dispatch
        // El título hay que asignarlo antes de asignar los colores.
        appWindow.Title = "Trucos MAUI by elGuille";
        // Este es el color que tiene en mi equipo la barra de título.
        appWindow.TitleBar.BackgroundColor = Microsoft.UI.ColorHelper.FromArgb(255, 0, 120, 212);
        appWindow.TitleBar.ForegroundColor = Microsoft.UI.Colors.White;
    

    Y con esto lo dejo por hoy… voy a seguir investigando (y probando) para poder ponerte algunos trucos más.

    El código coloreado usando el condicional de WINDOWS (#if WINDOWS)

    Pues eso, que habitualmente se muestra con el color grisáceo ese que te he mostrado antes cuando usas el condicional de compilación para Windows (#if WINDOWS) y, algunas veces, no sé cómo, sale coloreado (que es como debería salir).

    Este código es de otra aplicación pero intentaré usar los mismos valores que en este proyecto de pruebas.

                // Indicar el tamaño para la app de Windows.
                Microsoft.Maui.Handlers.WindowHandler.Mapper.AppendToMapping(nameof(IWindow), (handler, view) =>
                {
    #if WINDOWS
    
                    // Asignar manualmente el tamaño. 
                    int winWidth = 800; // 1700; // 2800;
                    int winHeight = 640; //1800
    
                    var mauiWindow = handler.VirtualView;
                    var nativeWindow = handler.PlatformView;
                    nativeWindow.Activate();
                    IntPtr windowHandle = WinRT.Interop.WindowNative.GetWindowHandle(nativeWindow);
                    var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(windowHandle);
                    var appWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId);
                    //appWindow.Resize(new Windows.Graphics.SizeInt32(winWidth, winHeight));
    
                    // get screen size
                    DisplayInfo disp = DeviceDisplay.Current.MainDisplayInfo;
                    double x, y;
    
                    // dispatcher is used to give the window time to actually resize
                    Dispatcher.Dispatch(() =>
                    {
                        disp = DeviceDisplay.Current.MainDisplayInfo;
                        
                        // Si Density es diferente de 1, ajustar el tamaño.
                        if (disp.Density > 1)
                        {
                            winWidth = (int)(winWidth * disp.Density);
                            winHeight = (int)(winHeight * disp.Density);
                        }
                        // El tamaño de la pantalla de este equipo.
                        int screenW = (int)(disp.Width / disp.Density);
                        int screenH = (int)(disp.Height / disp.Density);
                        // Si el alto indicado es mayor, ponerlo para que entre en esta pantalla.
                        if (winHeight > screenH)
                        {
                            winHeight = screenH - 60;
                        }
                        // Si el ancho indicado es mayor, ponerlo para que entre en esta pantalla.
                        if (winWidth > screenW)
                        {
                            winWidth = screenW - 60;
                        }
                        appWindow.Resize(new Windows.Graphics.SizeInt32(winWidth, winHeight));
                        x = (screenW - winWidth) / 2;
                        if (x < 0) 
                        {
                            x = 0;
                        }
                        y = (screenH - winHeight - 40) / 2;
                        if (y < 0)
                        {
                            y = 0;
                        }
                        appWindow.Move(new Windows.Graphics.PointInt32((int)x, (int)y));
    
                        // El título hay que asignarlo antes de asignar los colores.
                        appWindow.Title = "Trucos MAUI by elGuille";
                        // Este es el color que tiene en mi equipo la barra de título.
                        appWindow.TitleBar.BackgroundColor = Microsoft.UI.ColorHelper.FromArgb(255, 0, 120, 212);
                        appWindow.TitleBar.ForegroundColor = Microsoft.UI.Colors.White;
                    });
    
    #endif
                });
    
    

    Y, ya sabes, si te parece bien, puedes hacer un donativo con PayPal, que es como si me invitaras a un refresco, es decir, no es necesario que me dejes toda tu herencia, solo un par de euritos de nada… 😉

    Nos vemos.
    Guillermo

    P.S.
    El repositorio de GitHub ya está creado: Trucos_MAUI.

    P.S.2
    Por cierto, ahora no me funciona la app para iOS.
    El error que da es:

    Error CS1705: Assembly ‘Microsoft.Maui’ with identity ‘Microsoft.Maui, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’ uses ‘Microsoft.iOS, Version=16.0.0.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065’ which has a higher version than referenced assembly ‘Microsoft.iOS’ with identity ‘Microsoft.iOS, Version=15.4.300.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065’

    He buscado (con BING, ya que con Google no encontraba nada al poner esa cadena) para ver de qué va esto y lo que he encontrado (https://github.com/dotnet/maui/issues/8858) dice que ya está resuelto (o algo así) y en teoría la solución que da a mí no me funciona, que si no he entendido mal es dotnet workload install (supongo que indicando o maui o ios) pero nada, también he probado con dotnet workload install ios, con dotnet workload update, con dotnet workload repair y posicionándome en el directorio del proyecto con dotnet workload restore Trucos_MAUI.csproj, pero nada de nada… Ni siquiera usando el peasso de comando este:
    dotnet workload install maui --from-rollback-file https://aka.ms/dotnet/maui/6.0.408.json --source https://aka.ms/dotnet6/nuget/index.json --source https://api.nuget.org/v3/index.json
    Pero nada de nada… ya, por último, hasta he desinstalado el Visual Studio (en realidad he usado la opción Rollback to previous version) la versión 17.3.6 (a la 17.3.5) pero tampoco ha solucionado nada de nada, así que… he vuelto a instalar la versión 17.3.6, que es la última a día de hoy.
    A ver si para la siguiente tanda de trucos tengo la solución. ????

    P.S.3 (12-oct-22 17.35)
    He creado otra entrada (Errores de iOS con .NET MAUI) con el problema este que te comento en el «P.S.2» con idea de ver si lo soluciono.