您现在的位置是:网站首页> .NET Core

MAUI与Blazor学习经验总结

摘要

MAUI与Blazor学习经验总结


MAUI学习经验总结

Blazor学习经验总结



MAUI学习经验总结

学习笔记

MAUI基础组件

MAUI的布局组件

在运行时加载 XAML

数据绑定基础

数据绑定和 MVVM



MAUI基础组件

MAUI(Multi-platform App UI)提供了丰富的UI组件,用于构建跨平台的应用程序界面。以下是MAUI中常用的UI组件:

Label:

用于显示文本的控件。

支持设置文本内容、字体、颜色、对齐方式等属性。

Button:

表示可点击的按钮控件。

可以设置文本、图标、样式等属性。

提供点击事件用于处理用户交互。

Entry:

用于接受用户输入的单行文本框控件。

可以设置占位符、键盘类型、最大长度等属性。

Editor:

用于接受用户输入的多行文本框控件。

支持设置文本内容、字体、颜色等属性。

CheckBox:

表示复选框控件,用于选择或取消选择某个选项。

可以设置选中状态、文本内容等属性。

RadioButton:

表示单选按钮控件,用于在多个选项中选择一个。

可以设置选中状态、文本内容等属性。

Slider:

表示滑块控件,用于在指定范围内选择一个值。

可以设置最小值、最大值、步长等属性。

Switch:

表示开关控件,用于切换某个选项的开启或关闭状态。

可以设置开启/关闭状态、颜色等属性。

Picker:

表示选择器控件,用于从多个选项中选择一个值。

可以设置选项列表、选中项等属性。

DatePicker:

表示日期选择器控件,用于选择日期。

可以设置日期范围、格式等属性。

TimePicker:

表示时间选择器控件,用于选择时间。

可以设置时间范围、格式等属性。

ListView:

用于显示项目列表的控件。

支持数据绑定、项目模板、选择事件等功能。

CollectionView:

用于显示项目集合的控件,提供更灵活的布局和自定义选项。

支持数据绑定、项目模板、选择事件等功能。

CarouselView:

用于显示可滚动的项目轮播图的控件。

支持自动播放、循环、指示器等功能。

Image:

用于显示图像的控件。

支持本地图像和网络图像的加载和显示。

WebView:

用于在应用程序中嵌入和显示网页内容的控件。

支持导航、加载HTML内容等功能。

除了这些常用的UI组件,MAUI还提供了其他一些专业的控件,如地图控件、图表控件、条形码扫描控件等,用于满足特定的应用场景需求。



MAUI的布局组件

MAUI(Multi-platform App UI)提供了多种布局组件,用于构建响应式和灵活的用户界面。以下是 MAUI 中常用的布局组件:

StackLayout:

以垂直或水平方向堆叠子元素的布局。

子元素按照添加的顺序依次排列。

可以设置间距、对齐方式等属性。

Grid:

将子元素放置在由行和列定义的网格中的布局。

可以灵活地控制行和列的大小、比例、对齐方式等。

支持合并单元格和设置行列间距。

FlexLayout:

基于 CSS Flexbox 布局模型的布局容器。

提供了灵活的布局选项,如对齐、换行、排列方向等。

适用于创建自适应和响应式的界面布局。

AbsoluteLayout:

通过指定子元素的绝对位置和大小来进行布局。

子元素的位置可以使用像素值或相对于容器的比例来指定。

适用于需要精确控制元素位置的场景。

RelativeLayout:

通过相对位置关系来布局子元素。

可以相对于容器或其他子元素来定位。

支持设置锚点、对齐方式、偏移量等。

ScrollView:

提供滚动功能的布局容器。

当子元素的内容超出可见区域时,可以滚动查看。

可以设置滚动方向、滚动条可见性等属性。

Frame:

用于为单个子元素提供边框和背景的装饰性布局。

可以设置边框样式、颜色、阴影等属性。

ContentView:

用于包装单个子元素的简单布局。

可以用作自定义控件的基类或用于分组和样式化。

Border:

为子元素提供边框和背景的装饰性布局。

类似于 Frame,但提供了更灵活的边框设置选项。


除了这些内置的布局组件,MAUI 还支持自定义布局和组合使用多个布局来创建复杂的界面结构。你可以根据具体的界面设计需求选择合适的布局组件,并通过组合和嵌套布局来实现所需的布局效果。

同时,MAUI 还提供了一些布局相关的属性和方法,如 Margin、Padding、Alignment、Spacing 等,用于进一步控制和调整布局的样式和行为。




在运行时加载 XAML

string navigationButtonXAML = "<Button Text=\"Navigate\" />";

Button navigationButton = new Button().LoadFromXaml(navigationButtonXAML);

...

stackLayout.Add(navigationButton);



string pageXAML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<ContentPage xmlns=\"http://schemas.microsoft.com/dotnet/2021/maui\"\nxmlns:x=\"http://schemas.microsoft.com/winfx/2009/xaml\"\nx:Class=\"LoadRuntimeXAML.CatalogItemsPage\"\nTitle=\"Catalog Items\">\n</ContentPage>";

ContentPage page = new ContentPage().LoadFromXaml(pageXAML);

await Navigation.PushAsync(page);



访问元素

string pageXAML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<ContentPage xmlns=\"http://schemas.microsoft.com/dotnet/2021/maui\"\nxmlns:x=\"http://schemas.microsoft.com/winfx/2009/xaml\"\nx:Class=\"LoadRuntimeXAML.CatalogItemsPage\"\nTitle=\"Catalog Items\">\n<StackLayout>\n<Label x:Name=\"monkeyName\"\n />\n</StackLayout>\n</ContentPage>";

ContentPage page = new ContentPage().LoadFromXaml(pageXAML);


Label monkeyLabel = page.FindByName<Label>("monkeyName");

monkeyLabel.Text = "Seated Monkey";



数据绑定基础

数据绑定连接两个对象的属性,即源和目标。 在代码中,以下两个步骤是必需的:

1.目标对象的 BindingContext 属性必须设置为源对象,

2.必须在目标对象上调用 SetBinding 方法(通常与 Binding 类结合使用),将该对象的属性绑定到源对象的属性。


视图与视图绑定

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

             x:Class="XamlSamples.SliderBindingsPage"

             Title="Slider Bindings Page">

    <StackLayout>

        <Label Text="ROTATION"

               x:Name="label"

               BindingContext="{x:Reference slider}"

               Rotation="{Binding Path=Value}"

               FontAttributes="Bold"

               FontSize="18"

               HorizontalOptions="Center"

               VerticalOptions="Center" />

        <Slider x:Name="slider"

                Maximum="360"

                VerticalOptions="Center" />

        <Label BindingContext="{x:Reference slider}"

               Text="{Binding Value, StringFormat='The angle is {0:F0} degrees'}"

               FontAttributes="Bold"

               FontSize="18"

               HorizontalOptions="Center"

               VerticalOptions="Center" />

    </StackLayout>

</ContentPage>


用代码方式:

InitializeComponent();

label.BindingContext = slider;

label.SetBinding(Label.RotationProperty, "Value");



ListView的一个例子

using System.Reflection;

using System.Text;


namespace XamlSamples

{

    public class NamedColor

    {

        public string Name { get; private set; }

        public string FriendlyName { get; private set; }

        public Color Color { get; private set; }


        // Expose the Color fields as properties

        public float Red => Color.Red;

        public float Green => Color.Green;

        public float Blue => Color.Blue;


        public static IEnumerable<NamedColor> All { get; private set; }


        static NamedColor()

        {

            List<NamedColor> all = new List<NamedColor>();

            StringBuilder stringBuilder = new StringBuilder();


            // Loop through the public static fields of the Color structure.

            foreach (FieldInfo fieldInfo in typeof(Colors).GetRuntimeFields())

            {

                if (fieldInfo.IsPublic &&

                    fieldInfo.IsStatic &&

                    fieldInfo.FieldType == typeof(Color))

                {

                    // Convert the name to a friendly name.

                    string name = fieldInfo.Name;

                    stringBuilder.Clear();

                    int index = 0;


                    foreach (char ch in name)

                    {

                        if (index != 0 && Char.IsUpper(ch))

                        {

                            stringBuilder.Append(' ');

                        }

                        stringBuilder.Append(ch);

                        index++;

                    }


                    // Instantiate a NamedColor object.

                    NamedColor namedColor = new NamedColor

                    {

                        Name = name,

                        FriendlyName = stringBuilder.ToString(),

                        Color = (Color)fieldInfo.GetValue(null)

                    };


                    // Add it to the collection.

                    all.Add(namedColor);

                }

            }

            all.TrimExcess();

            All = all;

        }

    }

}


使用 x:Static 标记扩展可以将静态 NamedColor.All 属性设置为 ListView 的 ItemsSource:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

             xmlns:local="clr-namespace:XamlSamples"

             x:Class="XamlSamples.ListViewDemoPage"

             Title="ListView Demo Page">

    <ContentPage.Resources>

        <x:Double x:Key="boxSize">50</x:Double>

        <x:Int32 x:Key="rowHeight">60</x:Int32>

        <local:FloatToIntConverter x:Key="intConverter" />

    </ContentPage.Resources>


    <ListView ItemsSource="{x:Static local:NamedColor.All}"

              RowHeight="{StaticResource rowHeight}">

        <ListView.ItemTemplate>

            <DataTemplate>

                <ViewCell>

                    <StackLayout Padding="5, 5, 0, 5"

                                 Orientation="Horizontal"

                                 Spacing="15">

                        <BoxView WidthRequest="{StaticResource boxSize}"

                                 HeightRequest="{StaticResource boxSize}"

                                 Color="{Binding Color}" />

                        <StackLayout Padding="5, 0, 0, 0"

                                     VerticalOptions="Center">

                            <Label Text="{Binding FriendlyName}"

                                   FontAttributes="Bold"

                                   FontSize="14" />

                            <StackLayout Orientation="Horizontal"

                                         Spacing="0">

                                <Label Text="{Binding Red,

                                                      Converter={StaticResource intConverter},

                                                      ConverterParameter=255,

                                                      StringFormat='R={0:X2}'}" />                                

                                <Label Text="{Binding Green,

                                                      Converter={StaticResource intConverter},

                                                      ConverterParameter=255,

                                                      StringFormat=', G={0:X2}'}" />                                

                                <Label Text="{Binding Blue,

                                                      Converter={StaticResource intConverter},

                                                      ConverterParameter=255,

                                                      StringFormat=', B={0:X2}'}" />

                            </StackLayout>

                        </StackLayout>

                    </StackLayout>

                </ViewCell>

            </DataTemplate>

        </ListView.ItemTemplate>

    </ListView>

</ContentPage>



数据绑定和 MVVM

固定简单的绑定

基础绑定

稍微复杂点儿

命令绑定



固定简单的绑定

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

             xmlns:sys="clr-namespace:System;assembly=netstandard"

             x:Class="XamlSamples.OneShotDateTimePage"

             Title="One-Shot DateTime Page">


    <VerticalStackLayout BindingContext="{x:Static sys:DateTime.Now}"

                         Spacing="25" Padding="30,0"

                         VerticalOptions="Center" HorizontalOptions="Center">


        <Label Text="{Binding Year, StringFormat='The year is {0}'}" />

        <Label Text="{Binding StringFormat='The month is {0:MMMM}'}" />

        <Label Text="{Binding Day, StringFormat='The day is {0}'}" />

        <Label Text="{Binding StringFormat='The time is {0:T}'}" />


    </VerticalStackLayout>


</ContentPage>



基础绑定

using System.ComponentModel;

using System.Runtime.CompilerServices;


namespace XamlSamples;


class ClockViewModel: INotifyPropertyChanged

{

    public event PropertyChangedEventHandler PropertyChanged;


    private DateTime _dateTime;

    private Timer _timer;


    public DateTime DateTime

    {

        get => _dateTime;

        set

        {

            if (_dateTime != value)

            {

                _dateTime = value;

                OnPropertyChanged(); // reports this property

            }

        }

    }


    public ClockViewModel()

    {

        this.DateTime = DateTime.Now;


        // Update the DateTime property every second.

        _timer = new Timer(new TimerCallback((s) => this.DateTime = DateTime.Now),

                           null, TimeSpan.Zero, TimeSpan.FromSeconds(1));

    }


    ~ClockViewModel() =>

        _timer.Dispose();


    public void OnPropertyChanged([CallerMemberName] string name = "") =>

        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

}

视图模型通常实现 INotifyPropertyChanged 接口,该接口可让类在其任何一个属性发生更改时引发 PropertyChanged 事件。

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

             xmlns:local="clr-namespace:XamlSamples"

             x:Class="XamlSamples.ClockPage"

             Title="Clock Page">

    <ContentPage.BindingContext>

        <local:ClockViewModel />

    </ContentPage.BindingContext>


    <Label Text="{Binding DateTime, StringFormat='{0:T}'}"

           FontSize="18"

           HorizontalOptions="Center"

           VerticalOptions="Center" />

</ContentPage>


稍微复杂点儿

using System.ComponentModel;

using System.Runtime.CompilerServices;


namespace XamlSamples;


class HslViewModel: INotifyPropertyChanged

{

    public event PropertyChangedEventHandler PropertyChanged;


    private float _hue, _saturation, _luminosity;

    private Color _color;


    public float Hue

    {

        get => _hue;

        set

        {

            if (_hue != value)

                Color = Color.FromHsla(value, _saturation, _luminosity);

        }

    }


    public float Saturation

    {

        get => _saturation;

        set

        {

            if (_saturation != value)

                Color = Color.FromHsla(_hue, value, _luminosity);

        }

    }


    public float Luminosity

    {

        get => _luminosity;

        set

        {

            if (_luminosity != value)

                Color = Color.FromHsla(_hue, _saturation, value);

        }

    }


    public Color Color

    {

        get => _color;

        set

        {

            if (_color != value)

            {

                _color = value;

                _hue = _color.GetHue();

                _saturation = _color.GetSaturation();

                _luminosity = _color.GetLuminosity();


                OnPropertyChanged("Hue");

                OnPropertyChanged("Saturation");

                OnPropertyChanged("Luminosity");

                OnPropertyChanged(); // reports this property

            }

        }

    }


    public void OnPropertyChanged([CallerMemberName] string name = "") =>

        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

}


XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

             xmlns:local="clr-namespace:XamlSamples"

             x:Class="XamlSamples.HslColorScrollPage"

             Title="HSL Color Scroll Page">

    <ContentPage.BindingContext>

        <local:HslViewModel Color="Aqua" />

    </ContentPage.BindingContext>


    <VerticalStackLayout Padding="10, 0, 10, 30">

        <BoxView Color="{Binding Color}"

                 HeightRequest="100"

                 WidthRequest="100"

                 HorizontalOptions="Center" />

        <Label Text="{Binding Hue, StringFormat='Hue = {0:F2}'}"

               HorizontalOptions="Center" />

        <Slider Value="{Binding Hue}"

                Margin="20,0,20,0" />

        <Label Text="{Binding Saturation, StringFormat='Saturation = {0:F2}'}"

               HorizontalOptions="Center" />

        <Slider Value="{Binding Saturation}"

                Margin="20,0,20,0" />

        <Label Text="{Binding Luminosity, StringFormat='Luminosity = {0:F2}'}"

               HorizontalOptions="Center" />

        <Slider Value="{Binding Luminosity}"

                Margin="20,0,20,0" />

    </VerticalStackLayout>

</ContentPage>


命令

接口ICommand

using System.ComponentModel;

using System.Runtime.CompilerServices;

using System.Windows.Input;


namespace XamlSamples;


class KeypadViewModel: INotifyPropertyChanged

{

    public event PropertyChangedEventHandler PropertyChanged;


    private string _inputString = "";

    private string _displayText = "";

    private char[] _specialChars = { '*', '#' };


    public ICommand AddCharCommand { get; private set; }

    public ICommand DeleteCharCommand { get; private set; }


    public string InputString

    {

        get => _inputString;

        private set

        {

            if (_inputString != value)

            {

                _inputString = value;

                OnPropertyChanged();

                DisplayText = FormatText(_inputString);


                // Perhaps the delete button must be enabled/disabled.

                ((Command)DeleteCharCommand).ChangeCanExecute();

            }

        }

    }


    public string DisplayText

    {

        get => _displayText;

        private set

        {

            if (_displayText != value)

            {

                _displayText = value;

                OnPropertyChanged();

            }

        }

    }


    public KeypadViewModel()

    {

        // Command to add the key to the input string

        AddCharCommand = new Command<string>((key) => InputString += key);


        // Command to delete a character from the input string when allowed

        DeleteCharCommand =

            new Command(

                // Command will strip a character from the input string

                () => InputString = InputString.Substring(0, InputString.Length - 1),


                // CanExecute is processed here to return true when there's something to delete

                () => InputString.Length > 0

            );

    }


    string FormatText(string str)

    {

        bool hasNonNumbers = str.IndexOfAny(_specialChars) != -1;

        string formatted = str;


        // Format the string based on the type of data and the length

        if (hasNonNumbers || str.Length < 4 || str.Length > 10)

        {

            // Special characters exist, or the string is too small or large for special formatting

            // Do nothing

        }


        else if (str.Length < 8)

            formatted = string.Format("{0}-{1}", str.Substring(0, 3), str.Substring(3));


        else

            formatted = string.Format("({0}) {1}-{2}", str.Substring(0, 3), str.Substring(3, 3), str.Substring(6));


        return formatted;

    }



    public void OnPropertyChanged([CallerMemberName] string name = "") =>

        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

}


XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

             xmlns:local="clr-namespace:XamlSamples"

             x:Class="XamlSamples.KeypadPage"

             Title="Keypad Page">

    <ContentPage.BindingContext>

        <local:KeypadViewModel />

    </ContentPage.BindingContext>


    <Grid HorizontalOptions="Center" VerticalOptions="Center">

        <Grid.RowDefinitions>

            <RowDefinition Height="Auto" />

            <RowDefinition Height="Auto" />

            <RowDefinition Height="Auto" />

            <RowDefinition Height="Auto" />

            <RowDefinition Height="Auto" />

        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>

            <ColumnDefinition Width="80" />

            <ColumnDefinition Width="80" />

            <ColumnDefinition Width="80" />

        </Grid.ColumnDefinitions>


        <Label Text="{Binding DisplayText}"

               Margin="0,0,10,0" FontSize="20" LineBreakMode="HeadTruncation"

               VerticalTextAlignment="Center" HorizontalTextAlignment="End"

               Grid.ColumnSpan="2" />


        <Button Text="&#x21E6;" Command="{Binding DeleteCharCommand}" Grid.Column="2"/>


        <Button Text="1" Command="{Binding AddCharCommand}" CommandParameter="1" Grid.Row="1" />

        <Button Text="2" Command="{Binding AddCharCommand}" CommandParameter="2" Grid.Row="1" Grid.Column="1" />

        <Button Text="3" Command="{Binding AddCharCommand}" CommandParameter="3" Grid.Row="1" Grid.Column="2" />


        <Button Text="4" Command="{Binding AddCharCommand}" CommandParameter="4" Grid.Row="2" />

        <Button Text="5" Command="{Binding AddCharCommand}" CommandParameter="5" Grid.Row="2" Grid.Column="1" />

        <Button Text="6" Command="{Binding AddCharCommand}" CommandParameter="6" Grid.Row="2" Grid.Column="2" />


        <Button Text="7" Command="{Binding AddCharCommand}" CommandParameter="7" Grid.Row="3" />

        <Button Text="8" Command="{Binding AddCharCommand}" CommandParameter="8" Grid.Row="3" Grid.Column="1" />

        <Button Text="9" Command="{Binding AddCharCommand}" CommandParameter="9" Grid.Row="3" Grid.Column="2" />


        <Button Text="*" Command="{Binding AddCharCommand}" CommandParameter="*" Grid.Row="4" />

        <Button Text="0" Command="{Binding AddCharCommand}" CommandParameter="0" Grid.Row="4" Grid.Column="1" />

        <Button Text="#" Command="{Binding AddCharCommand}" CommandParameter="#" Grid.Row="4" Grid.Column="2" />

    </Grid>

</ContentPage>














Blazor学习经验总结

学习笔记










Top