【问题标题】:Xamarin Forms MVVM update not working for UWPXamarin Forms MVVM 更新不适用于 UWP
【发布时间】:2017-12-04 20:12:09
【问题描述】:

我有以下代码:

 Device.StartTimer(TimeSpan.FromSeconds(1),
     () =>
     {

     foreach (var detail in Customer.Details.Where(p => new string[] { "Queue", "Wating"}.Contains(p.Status)))
     {
         detail.HasTimer = true;
         detail.Time = detail.Time.Add(TimeSpan.FromSeconds(1));

     }
     return true;
     });

当我将这段代码部署到 android 时,它运行良好,但在 UWP 中,信息只是在 UI 中被删除。

我也尝试过使用 Device.BeginInvokeOnMainThread 方法,但结果相同。

每个属性都实现了 INotifyPropertyChanged,而细节是一个也实现了 INotifyPropertyChanged 的​​ ObservableCollection

我正在使用 Visual Studio 2017 社区

XAML 如下:

<ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Bulingo.CCC.Mobile.Views.TicketDigital">
  <ContentView.Content>
    <StackLayout >
      <StackLayout.VerticalOptions>
        <LayoutOptions Alignment="Center">

        </LayoutOptions>
      </StackLayout.VerticalOptions>

      <Button IsVisible="{Binding MostrarBoton}"
            Text="Leer Código"
            VerticalOptions="Center"
            HorizontalOptions="Center"
            TextColor="White"          
            BackgroundColor="#77D065"
            Command="{Binding ReadQRCodeCommand}"/>

      <Label FontAttributes="Bold"
         TextColor="Red"
         Text="El Código QR leído no es válido."
         IsVisible="{Binding MostrarErrorQrCode}"/>
      <Label FontAttributes="Bold"
         TextColor="Red"
         Text="Por favor verifique su conexión de red."
         IsVisible ="{Binding MostrarErrorConexion}"
           />
      <StackLayout VerticalOptions="FillAndExpand" IsVisible="{Binding MostrarServicios}">

        <Image Aspect="Fill">
          <Image.Source>
            <UriImageSource Uri="{Binding LogoUrl}" />
          </Image.Source>
        </Image>
        <StackLayout Orientation="Horizontal">
          <Label FontSize="Large">Número</Label>
          <Label Text="{Binding Cliente.Numero}" FontSize="Large"/>
        </StackLayout>


        <ListView ItemsSource="{Binding Cliente.Detalles}"  SeparatorColor="Yellow" RowHeight="160">
          <ListView.ItemTemplate>
            <DataTemplate>
              <ViewCell BindingContext="{Binding}" >
                <Frame OutlineColor="Accent"  >
                  <ContentView Padding="1" Margin="2"  >
                    <StackLayout   >
                      <Label Text="{Binding Servicio}" FontSize="Medium"/>
                      <StackLayout Orientation="Horizontal"  >
                        <Label Text="Sala de Espera"/>
                        <Label Text="{Binding SalaEspera}" HorizontalOptions="End"/>
                      </StackLayout>
                      <StackLayout Orientation="Horizontal"   >
                        <Label Text="Tiempo Transcurrido"/>
                        <Label  Text="{Binding TiempoTranscurridoText}" TextColor="{Binding TiempoTranscurridoColor}"/>
                      </StackLayout>
                      <StackLayout Orientation="Horizontal"  >
                        <Label Text="Personas delante"/>
                        <Label Text="{Binding TotalEnCola}"/>
                      </StackLayout>
                      <StackLayout Orientation="Horizontal"  >
                        <Label Text="Estado"/>
                        <Label Text="{Binding Estado}" FontAttributes="Bold"/>
                      </StackLayout>
                    </StackLayout>
                  </ContentView>
                </Frame>

              </ViewCell>
            </DataTemplate>
          </ListView.ItemTemplate>
        </ListView>


      </StackLayout>

      <StackLayout VerticalOptions="FillAndExpand" IsVisible="{Binding MostrarFormularioEncuesta}">
        <Button HorizontalOptions="End"
          Command="{Binding CancelSurveyCommand}"
          Text="No quiero llenar la encuesta..."/>
        <Button Command="{Binding SaveSurveyCommand}" Text="Enviar"
             TextColor="White"
             BackgroundColor="#77D065"
             HorizontalOptions="End"/>
        <TableView Intent="Form">
          <TableRoot Title="Llenar Encuesta" BindingContext="{Binding Encuesta}">

            <TableSection Title="Método preferido">
              <ViewCell>
                <Picker SelectedIndex="{Binding BetterOrWorseInt}">
                  <Picker.Items>
                    <x:String>No estoy Seguro</x:String>
                    <x:String>Sin Sistema</x:String>
                    <x:String>Con Sistema</x:String>
                  </Picker.Items>
                </Picker>
              </ViewCell>
            </TableSection>
            <TableSection Title="Con el nuevo sistema, ¿la atención es más rápida o más lenta?">
              <ViewCell>
                <Picker SelectedIndex="{Binding SpeedInt}">
                  <Picker.Items>
                    <x:String>No estoy Seguro</x:String>
                    <x:String>Más lenta</x:String>
                    <x:String>Más rápida</x:String>
                  </Picker.Items>
                </Picker>
              </ViewCell>
            </TableSection>
            <TableSection Title="¿Desearía que instalemos el sistema en otras áreas o sucursales?">
              <ViewCell>
                <Picker SelectedIndex="{Binding ExpandInt}">
                  <Picker.Items>
                    <x:String>No estoy Seguro</x:String>
                    <x:String>No, no me gustaría</x:String>
                    <x:String>Sí, me gustaría</x:String>
                  </Picker.Items>
                </Picker>
              </ViewCell>
            </TableSection>
            <TableSection Title="¿Qué piensa de la calidad del nuevo sistema?">
              <ViewCell>
                <StackLayout HorizontalOptions="Fill">
                  <AbsoluteLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
                    <Slider Minimum="0" Maximum="5" WidthRequest="200" Value="{Binding QualityDouble}" Scale="1"
          AbsoluteLayout.LayoutBounds="0, 0.5"
          AbsoluteLayout.LayoutFlags="PositionProportional"
                      />
                    <Label Text="{Binding QualityText}" HorizontalOptions="Center" HorizontalTextAlignment="Center"
         AbsoluteLayout.LayoutBounds="0, 1"
         AbsoluteLayout.LayoutFlags="PositionProportional"
                         />
                  </AbsoluteLayout>
                </StackLayout>
              </ViewCell>
            </TableSection>
            <TableSection Title="¿Qué tan fácil es adaptarse al nuevo sistema?">
              <ViewCell>
                <StackLayout HorizontalOptions="Fill">
                  <AbsoluteLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
                    <Slider Minimum="0" Maximum="5" WidthRequest="200" Value="{Binding EasinessDouble}" Scale="1"
          AbsoluteLayout.LayoutBounds="0, 0.5"
          AbsoluteLayout.LayoutFlags="PositionProportional"
                      />
                    <Label Text="{Binding EasinessText}" HorizontalOptions="Center" HorizontalTextAlignment="Center"
         AbsoluteLayout.LayoutBounds="0, 1"
         AbsoluteLayout.LayoutFlags="PositionProportional"
                         />
                  </AbsoluteLayout>
                </StackLayout>
              </ViewCell>
            </TableSection>
            <TableSection Title="Sugerencias para el Sistema">
              <ViewCell >
                <Editor Text="{Binding NotesForBulingo}" HeightRequest="100" />


              </ViewCell>
            </TableSection>
            <TableSection Title="Sugerencias general">
              <ViewCell>
                <Editor Text="{Binding NotesForClient}" HeightRequest="100"/>
              </ViewCell>
            </TableSection>

          </TableRoot>
        </TableView>

      </StackLayout>

      <StackLayout VerticalOptions="FillAndExpand" IsVisible="{Binding MostrarAgradecimiento}">
        <Label FontSize="Large"
         FontAttributes="Bold"
         Text="¡Gracias por usar nuestros servicios y gracias por llenar nuestra encuesta!"></Label>

        <Image Aspect="AspectFill">
          <Image.Source>
            <UriImageSource Uri="{Binding LogoUrl}" />
          </Image.Source>
        </Image>
        <StackLayout Orientation="Horizontal">
          <Label FontSize="Large">Número</Label>
          <Label Text="{Binding Cliente.Numero}" FontSize="Large"/>
        </StackLayout>


        <ListView ItemsSource="{Binding Cliente.Detalles}"  SeparatorColor="Yellow" RowHeight="160">
          <ListView.ItemTemplate>
            <DataTemplate>
              <ViewCell BindingContext="{Binding}" >
                <Frame OutlineColor="Accent"  >
                  <ContentView Padding="1" Margin="2"  >
                    <StackLayout   >
                      <Label Text="{Binding Servicio}" FontSize="Medium"/>
                      <StackLayout Orientation="Horizontal"  >
                        <Label Text="Sala de Espera"/>
                        <Label Text="{Binding SalaEspera}" HorizontalOptions="End"/>
                      </StackLayout>
                      <StackLayout Orientation="Horizontal"   >
                        <Label Text="Tiempo Transcurrido"/>
                        <Label  Text="{Binding TiempoTranscurridoText}" TextColor="{Binding TiempoTranscurridoColor}"/>
                      </StackLayout>
                      <StackLayout Orientation="Horizontal"  >
                        <Label Text="Personas delante"/>
                        <Label Text="{Binding TotalEnCola}"/>
                      </StackLayout>
                      <StackLayout Orientation="Horizontal"  >
                        <Label Text="Estado"/>
                        <Label Text="{Binding EstadoAtencion}" FontAttributes="Bold"/>
                      </StackLayout>
                    </StackLayout>

                  </ContentView>
                </Frame>

              </ViewCell>
            </DataTemplate>
          </ListView.ItemTemplate>
        </ListView>

      </StackLayout>
      <ActivityIndicator Color="Red" IsRunning="{Binding IsBusy}" />
    </StackLayout>

  </ContentView.Content>
</ContentView>

这是ViewModel,我删除了部分代码,因为我在这里达到了限制

    public class TicketDigitalViewModel : INotifyPropertyChanged
    {
        public static TimeSpan ServerClientTimeDifference { get; set; }
        private bool mostrarErrorQrCode;
        private static string surveyUri = "https://sdfghjklfghjkl.com/sadfkljsafdlkjroisdapi/api";
        private ClienteViewModel cliente;
        private bool isBusy;
        private string barCodeResult;
        private HubConnection connection;
        private IHubProxy ticketHub;
        private string connectionID;
        private ScreenStatuses screenStatus;
        private SampleViewModel encuesta;
        private bool isEncuestado;
        MobileBarcodeScanner scanner;
        bool scannerInitialized = false;
        private bool mostrarErrorConexion;
        internal void Resume()
        {
            initConnection();
        }

        public TicketDigitalViewModel()
        {
            Encuesta = new SampleViewModel();
            ReadQRCodeCommand = new Command(async () =>
                await ReadQRCode(),
                () => !IsBusy);
            SaveSurveyCommand = new Command(async () =>
                await SaveSurvey(Encuesta));
            CancelSurveyCommand = new Command(async () =>
                await CancelSurvey(),
                () => !IsBusy);
        }
        public Command CancelSurveyCommand { get; }
        public Command ReadQRCodeCommand { get; }
        public Command SaveSurveyCommand { get; }
        async Task<ZXing.Result> ReadQRCode()
        {
            if (!scannerInitialized)
            {
                scanner = new MobileBarcodeScanner();
                scannerInitialized = true;
            }
            BarCodeResult = null;
            var result = await scanner.Scan();
            BarCodeResult = result.Text;
            return result;
        }
        #region Data Properties.
        public ClienteViewModel Cliente
        {
            get
            {
                return cliente;
            }
            set
            {
                SetProperty(ref cliente, value);
                if (Cliente != null)
                {
                    if (Device.RuntimePlatform == "Windows")
                    {
                        Device.StartTimer(TimeSpan.FromSeconds(1),
                        () =>
                        {
                            foreach (var detalle in Cliente.Detalles.Where(p => new string[] { "EnCola", "Atendiendo", "EsperaCliente" }.Contains(p.Estado)))
                            {
                                Device.BeginInvokeOnMainThread(() =>
                                {
                                        detalle.HasTimer = true;
                                        detalle.TiempoTranscurrido = detalle.TiempoTranscurrido.Add(TimeSpan.FromSeconds(1));
                                });

                            }
                            return true;
                        });
                    }
                    else
                    {
                        Device.StartTimer(TimeSpan.FromSeconds(1),
                        () =>
                        {

                            foreach (var detalle in Cliente.Detalles.Where(p => new string[] { "EnCola", "Atendiendo", "EsperaCliente" }.Contains(p.Estado)))
                            {
                                detalle.HasTimer = true;
                                detalle.TiempoTranscurrido = detalle.TiempoTranscurrido.Add(TimeSpan.FromSeconds(1));

                            }
                            return true;
                        });

                        Encuesta.Office = Cliente.PartnerOffice;
                        Encuesta.Customer = Cliente.Partner;
                        encuesta.QueueID = Cliente.IdTurno;
                    }




                }
            }
        }

        public SampleViewModel Encuesta
        {
            get
            {
                return encuesta;
            }
            set
            {
                if (SetProperty(ref encuesta, value))
                {
                    encuesta = value;
                    OnPropertyChanged();
                }
            }
        }

        #endregion

        #region Process Properties

        internal ScreenStatuses ScreenStatus
        {
            get
            {
                return screenStatus;
            }
            set
            {
                screenStatus = value;
                OnPropertyChanged();
            }
        }

        public string ConnectionUrl
        {
            get
            {
                return BarCodeResult == null
                  ? null
                  : BarCodeResult.Split(new string[] { "/Atencion/Cliente" }, StringSplitOptions.None)[0];
            }
        }

        public string LogoUrl
        {
            get
            {
                return BarCodeResult == null
                    ? null
                    : ConnectionUrl + "/Home/Thumbnail?ImagePath=Logo.png&width=396&height=222";
            }
        }

        public string BarCodeResult
        {
            get
            {
                return barCodeResult;
            }
            set
            {
                try
                {
                    IsBusy = true;
                    var theValue = value;
#if DEBUG
                    theValue = theValue?.Replace("https", "http");
#endif

                    var scanned = value != null && barCodeResult == null;


                    if (SetProperty(ref barCodeResult, theValue))
                    {
                        if (value == null)
                        {
                            Task.Factory.StartNew(() =>
                            {
                                UpdateStatus();
                            });
                        }
                        else if (!Uri.IsWellFormedUriString(value, UriKind.Absolute) || !value.Contains("Cliente/TicketDigital/"))
                        {
                            BarCodeResult = null;
                            MostrarErrorQrCode = true;
                            IsBusy = false;
                        }
                        else
                        {
                            UpdateStatus();
                        }
                    }

                    if (scanned) Resume();


                }
                catch (Exception ex)
                {
                    throw;
                }
            }
        }

        public bool IsBusy
        {
            get
            {
                return isBusy;
            }
            set
            {
                if (SetProperty(ref isBusy, value))
                {
                    ReadQRCodeCommand.ChangeCanExecute();
                    SaveSurveyCommand.ChangeCanExecute();
                }


            }
        }

        public string ConnectionID
        {
            get
            {
                return connectionID;
            }
            set
            {
                connectionID = value;
                OnPropertyChanged();
            }
        }

        public bool MostrarBoton
        {
            get
            {
                return ScreenStatus == ScreenStatuses.BarCodeScanner;
            }
        }

        public bool MostrarFormularioEncuesta
        {
            get
            {
                return ScreenStatus == ScreenStatuses.Survey;
            }
        }

        public bool MostrarAgradecimiento
        {
            get
            {
                return ScreenStatus == ScreenStatuses.Finished;
            }
        }

        public bool MostrarServicios
        {
            get
            {
                return ScreenStatus == ScreenStatuses.TicketMonitoring;

            }
        }


        public bool IsEncuestado
        {
            get
            {
                return isEncuestado;
            }
            set
            {
                if (SetProperty(ref isEncuestado, value))
                {

                }

            }
        }

        public bool MostrarErrorQrCode
        {
            get
            {
                return mostrarErrorQrCode;
            }
            set
            {
                if (SetProperty(ref mostrarErrorQrCode, value))
                {

                }
            }
        }

        public bool MostrarErrorConexion
        {
            get
            {
                return mostrarErrorConexion;
            }
            set
            {
                if (SetProperty(ref mostrarErrorConexion, value))
                {

                }
            }
        }


        #endregion

        #region Methods.


        private void initConnection()
        {

                if (barCodeResult != null)
                {

                    if (Device.RuntimePlatform == "Windows")
                    {
                        IsBusy = true;
                        connection = new HubConnection(ConnectionUrl + "/signalr/hubs");

                        AsyncHelpers.RunSync(MonitorTicket);

                        IsBusy = false;


                    }
                    else
                    {
                        IsBusy = true;
                        connection = new HubConnection(ConnectionUrl + "/signalr/hubs");
                        Task.Factory.StartNew(async () =>
                        {
                                                           await MonitorTicket();

                                IsBusy = false;

                        });
                    }


                }
                else
                {
                    if (connection != null)
                        connection.Dispose();


                }

        }

        async Task MonitorTicket()
        {

                Cliente = await this.HttpRequest<ClienteViewModel>(HttpMethod.Get,
                    "/TurnoDetalleApi/DatosTicketImpresion",
                    string.Format("id={0}",
                        BarCodeResult.Split(new string[] { "id=" },
                        StringSplitOptions.None)[1])
                    );

                ticketHub = connection.CreateHubProxy("TicketHub");

                ticketHub.On<bool>("isAlive", (alive) =>
                {


                });

                ticketHub.On<ClienteDetalleViewModel>("update", async (detalle) =>
                {
                    if (detalle.Servicio == null)
                    {
                        try
                        {
                            detalle.Servicio = await this.HttpRequest<string>(HttpMethod.Get, "/ServicioApi/GetNombreServicio", $"IdServicio={detalle.IdServicio}");
                            detalle.SalaEspera = await this.HttpRequest<string>(HttpMethod.Get, "/SalaEsperaApi/GetNombre/", $"id={detalle.IdSalaEspera}");
                        }
                        catch (WebException)
                        {
                            BarCodeResult = null;
                            MostrarErrorConexion = true;
                        }
                    }

                    if (Cliente.Detalles
                        .Where(p => p.IdServicio == detalle.IdServicio)
                        .Any())
                    {
                        var miDetalle = Cliente.Detalles.Last(p => p.IdServicio == detalle.IdServicio);
                        miDetalle.Servicio = detalle.Servicio;
                        miDetalle.SalaEspera = detalle.SalaEspera;
                        miDetalle.Estado = detalle.Estado;
                        miDetalle.TiempoAtencionPromedio = detalle.TiempoAtencionPromedio;
                        miDetalle.TiempoEsperaRestante = detalle.TiempoEsperaRestante;
                        miDetalle.TotalEnCola = detalle.TotalEnCola;
                        miDetalle.TiempoTranscurrido = detalle.TiempoTranscurrido;

                    }
                    else
                    {
                        Device.StartTimer(TimeSpan.FromSeconds(1), () =>
                        {
                            detalle.HasTimer = true;
                            detalle.TiempoTranscurrido = detalle.TiempoTranscurrido.Add(TimeSpan.FromSeconds(1));

                            return true;
                        });
                        Cliente.Detalles.Add(detalle);

                    }
                    UpdateStatus();
                });

                await connection.Start();

                ConnectionID = connection.ConnectionId;


                var sample = await NetworkHelper.HttpRequest<SampleViewModel>("https://sdfghjklfghjkl.com/sadfkljsafdlkjroisdapi/api", HttpMethod.Get, "/survey", $"QueueID={Cliente.IdTurno}&Customer={Cliente.Partner}");
                IsEncuestado = sample != null;

                await ticketHub.Invoke("SetTicket", Cliente.IdTurno);
                await Task.Factory.StartNew(async () =>
                {
                    do
                    {

                        await ticketHub.Invoke("KeepAlive");

                        await Task.Delay(1000 * 60);

                    } while (ScreenStatus == ScreenStatuses.TicketMonitoring);
                });
                IsBusy = false;
                MostrarErrorConexion = false;


        }

        private void UpdateStatus()
        {

                if (BarCodeResult == null)
                {
                    ScreenStatus = ScreenStatuses.BarCodeScanner;
                }
                else if (Cliente != null && Cliente.Detalles.Any(p => new string[] { "EnCola", "Atendiendo", "EsperaCliente" }.Contains(p.Estado)) && !IsEncuestado)
                {
                    OnPropertyChanged(nameof(LogoUrl));

                    ScreenStatus = ScreenStatuses.TicketMonitoring;
                }
                else if (Cliente == null)
                {
                    ScreenStatus = ScreenStatuses.TicketMonitoring;
                }
                else if (!IsEncuestado)
                {
                    ScreenStatus = ScreenStatuses.Survey;
                }
                else
                {
                    ScreenStatus = ScreenStatuses.Finished;
                }

                OnPropertyChanged(nameof(ScreenStatus));
                OnPropertyChanged(nameof(MostrarAgradecimiento));
                OnPropertyChanged(nameof(MostrarBoton));
                OnPropertyChanged(nameof(MostrarFormularioEncuesta));
                OnPropertyChanged(nameof(MostrarServicios));
                IsBusy = false;


        }



        #endregion

        #region Events

        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged([CallerMemberName] string name = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }

        bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (Equals(storage, value))
                return false;
            storage = value;
            OnPropertyChanged(propertyName);
            return true;
        }


        #endregion


    }

【问题讨论】:

  • 什么是“detelle”?
  • 对不起,我只是把它翻译成英文,我忘了翻译那部分。
  • 除了时间绑定到 UI 之外,你还有什么其他的吗?你看到了吗?任何自定义渲染器?你能用绑定展示你的 xaml 吗?
  • 当我尝试在 UWP 中更新 ui 时有很多错误,但我认为它们都与同一个问题有关,所以我只发布了最简单的一个。例如,如果一个细节得到更新,所有关于该细节的 UI 信息都会被清除。
  • 所以你是说 UWP 绑定不起作用?我需要更多代码来重现并尝试确认。你能分享你的示例项目吗?没有所有信息,很难提供帮助

标签: c# mvvm xamarin.forms xamarin.uwp


【解决方案1】:

我终于可以让它工作了。

问题在于通用 Windows 平台的 xamarin 表单的当前“稳定”版本 (2.3.4.247)。

为了验证它,我下载了 Xamarin.Forms 的当前预发行版 2.3.5.256-pre,现在我的应用程序正在运行。

我在 Xamarin 论坛中得到了提示...https://forums.xamarin.com/discussion/comment/284777

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-22
    • 1970-01-01
    • 1970-01-01
    • 2019-04-23
    相关资源
    最近更新 更多