XAML 我正在为我的Xamarin表单应用程序创建一个全局聊天功能,但是由于某种原因,文本没有显示在UI上

yeotifhr  于 2023-01-03  发布在  其他
关注(0)|答案(1)|浏览(114)

当我查看我的UI时,元素按预期运行,但是绑定到我的视图模型的任何东西都没有显示,但是绑定似乎在工作。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Fate.Views.GlobalChatPage"
             x:DataType="viewmodels:GlobalChatPageViewModel"
             xmlns:viewmodels="clr-namespace:Fate.ViewModels"
             xmlns:converters="clr-namespace:Fate.Converters"
             BackgroundColor="#242022">
    
    <ContentPage.BindingContext>
        <viewmodels:GlobalChatPageViewModel />
    </ContentPage.BindingContext>

    <ContentPage.Resources>
        <ResourceDictionary>
            <converters:InverseBoolConverter x:Key="InverseBool" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <ContentPage.Content>
        <StackLayout>
            <StackLayout Orientation="Horizontal">
                <Button x:Name="ConnectButton" Text="Connect" Command="{Binding ConnectCommand}" />
                <Button x:Name="DisconnectButton" Text="Disconnect" Command="{Binding DisconnectCommand}" />
                <Entry IsReadOnly="False" x:Name="namenameEntry" Text="{Binding Name}" Placeholder="Enter Namename" />
            </StackLayout>
            <ListView x:Name="MessagesListView" ItemsSource="{Binding Messages}" HasUnevenRows="true" SelectionMode="None">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*" />
                                    <ColumnDefinition Width="*" />
                                </Grid.ColumnDefinitions>
                                <StackLayout Grid.Column="0" IsVisible="{Binding IsOwnMessage, Converter={StaticResource InverseBool}}">
                                    <Label Text="{Binding Name}" />
                                    <Frame x:Name="MessageFrame" CornerRadius="10" Padding="10" BackgroundColor="LightGray" HeightRequest="{Binding Source={x:Reference MessageLabel}, Path=Height}" Margin="0,0,0,20">
                                        <Label x:Name="MessageLabel" Text="{Binding Message}"/>
                                    </Frame>
                                </StackLayout>
                                <StackLayout Grid.Column="1" IsVisible="{Binding IsOwnMessage}">
                                    <Label Text="{Binding Name}" HorizontalOptions="FillAndExpand" />
                                    <Frame x:Name="MessageFrame2" CornerRadius="10" Padding="10" BackgroundColor="LightGray" HeightRequest="{Binding Source={x:Reference MessageLabel}, Path=Height}" Margin="0,0,0,20" HorizontalOptions="FillAndExpand">
                                        <Label x:Name="MessageLabel2" Text="{Binding Message}" HorizontalOptions="FillAndExpand" />
                                    </Frame>
                                </StackLayout>
                            </Grid>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            <StackLayout Orientation="Horizontal">
                <Entry IsReadOnly="False" x:Name="MessageEntry" Text="{Binding Message}"  HorizontalOptions="FillAndExpand" />
                <Button x:Name="SendButton" Text="Send" Command="{Binding SendMessageCommand}" />
            </StackLayout>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

这是我的视图模型:

using Microsoft.AspNetCore.SignalR.Client;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace Fate.ViewModels
{
    public class GlobalChatPageViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private string _name;
        private string _message;
        private ObservableCollection<string> _messages;
        private bool _isConnected;
        public bool _IsOwnMessage;
        public bool _IsSystemMessage;

        public bool IsSystemMessage
        {
            get
            {
                return _IsSystemMessage;
            }
            set
            {
                _IsSystemMessage = value;
                OnPropertyChanged();
            }
        }

        public bool IsOwnMessage
        {
            get
            {
                return _IsOwnMessage;
            }
            set
            {
                _IsOwnMessage = value;
                OnPropertyChanged();
            }
        }

        public string Name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
                OnPropertyChanged();
            }
        }

        public string Message
        {
            get
            {
                return _message;
            }
            set
            {
                _message = value;
                OnPropertyChanged();
            }
        }

        public ObservableCollection<string> Messages
        {
            get
            {
                return _messages;
            }
            set
            {
                _messages = value;
                OnPropertyChanged();
            }
        }

        public bool IsConnected
        {
            get
            {
                return _isConnected;
            }
            set
            {
                _isConnected = value;
                OnPropertyChanged();
            }
        }

        private HubConnection hubConnection;

        public Command SendMessageCommand { get; }
        public Command ConnectCommand { get; }
        public Command DisconnectCommand { get; }

        public GlobalChatPageViewModel()
        {
            Messages = new ObservableCollection<string>();
            SendMessageCommand = new Command(async () => { await SendMessage(Name, Message); });
            ConnectCommand = new Command(async () => await Connect());
            DisconnectCommand = new Command(async () => await Disconnect());

            IsConnected = false;

            hubConnection = new HubConnectionBuilder()
                .WithUrl($"https://placeholderlink/chatHub")
                .Build();

            hubConnection.On<string>("JoinChat", (user) =>
            {
                Messages.Add($"{user} has joined the chat");
                IsSystemMessage = true;
                IsOwnMessage = false;
            });

            hubConnection.On<string>("LeaveChat", (user) =>
            {
                Messages.Add($"{user} has left the chat");
                IsSystemMessage = true;
                IsOwnMessage = false;
            });

            hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
            {
                Messages.Add($"{user}: {message}");
                IsSystemMessage = false;
                if (Name == user)
                {
                    IsOwnMessage = true;
                }
            });

        }

        public async Task Connect()
        {
            if (IsConnected == false)
            {
                await hubConnection.StartAsync();
                await hubConnection.InvokeAsync("JoinChat", Name);

                IsConnected = true;
            }
        }

        public async Task SendMessage(string user, string message)
        {
            await hubConnection.InvokeAsync("SendMessage", user, message);
        }

        public async Task Disconnect()
        {
            if (IsConnected == true)
            {
                await hubConnection.InvokeAsync("LeaveChat", Name);
                await hubConnection.StopAsync();

                IsConnected = false;
            }
        }

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

这是我的转换器:

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Xamarin.Forms;

namespace Fate.Converters
{
    public class InverseBoolConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return !(bool)value;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return !(bool)value;
        }
    }
}

我希望每个消息的文本都显示在页面上,如果来自其他人,则显示在左侧,如果来自用户,则显示在右侧,但文本根本不显示。我检查了绑定上下文,得到了智能感知,没有构建错误,但它仍然不显示。

eanckbw9

eanckbw91#

问题1

您正在通过应用x:DataType属性来使用compiled bindings。因此,您还需要为视图层次结构中更低的任何DataTemplate设置正确的x:DataType,因为绑定上下文会发生变化。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Fate.Views.GlobalChatPage"
             x:DataType="viewmodels:GlobalChatPageViewModel"
             xmlns:viewmodels="clr-namespace:Fate.ViewModels"
             xmlns:converters="clr-namespace:Fate.Converters"
             BackgroundColor="#242022">
    
    <ContentPage.BindingContext>
        <viewmodels:GlobalChatPageViewModel />
    </ContentPage.BindingContext>

    <ContentPage.Resources>
        <ResourceDictionary>
            <converters:InverseBoolConverter x:Key="InverseBool" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <ContentPage.Content>
        <StackLayout>
            <StackLayout Orientation="Horizontal">
                <Button x:Name="ConnectButton" Text="Connect" Command="{Binding ConnectCommand}" />
                <Button x:Name="DisconnectButton" Text="Disconnect" Command="{Binding DisconnectCommand}" />
                <Entry IsReadOnly="False" x:Name="namenameEntry" Text="{Binding Name}" Placeholder="Enter Namename" />
            </StackLayout>
            <ListView x:Name="MessagesListView" ItemsSource="{Binding Messages}" HasUnevenRows="true" SelectionMode="None">
                <ListView.ItemTemplate>
                    <DataTemplate
                        x:DataType="YOUR_TYPE_HERE">
                        <ViewCell>
                            <!-- ... -->
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            <StackLayout Orientation="Horizontal">
                <Entry IsReadOnly="False" x:Name="MessageEntry" Text="{Binding Message}"  HorizontalOptions="FillAndExpand" />
                <Button x:Name="SendButton" Text="Send" Command="{Binding SendMessageCommand}" />
            </StackLayout>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

当它显示YOUR_TYPE_HERE时,您需要指定与传递给ItemsSource的集合相同的类型,在您的示例中为string(或其XAML等价物,即{x:Type x:String})。您还可以通过将DataTemplatex:DataType类型设置为{x:Null}来将绑定重置为经典绑定。

问题2

在您的DataTemplate中,您绑定到了该上下文中不存在的属性,因为您的ListView是用ObservableCollection<string>填充的。
例如,如果你想从ViewModel绑定到Name,你需要指定一个相对绑定:

<ViewCell>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <StackLayout Grid.Column="0">
            <Label Text="{Binding Name, Source={RelativeSource AncestorType={x:Type viewmodels:GlobalChatPageViewModel}}" />
            <!-- ... -->
        </StackLayout>
    </Grid>
</ViewCell>

备选

一个快速的修复方法可能是简单地删除ContentPage上的x:DataType

进一步观察

您多次绑定到Name,不清楚如何管理两个聊天参与者,以及每个参与者的姓名和消息来自何处,特别是因为ViewModel中只有一个Name属性和一个Messages集合。

相关问题