【问题标题】:Windows Phone - Binding View to View ModelWindows Phone - 将视图绑定到视图模型
【发布时间】:2013-01-23 17:12:30
【问题描述】:

所以,我正在学习 Windows Phone 的 MVVM 模式,并坚持如何将视图绑定到我的 ViewModel。我现在构建的应用程序正在获取当前和未来 5 天的天气,并使用 UserControl 将其显示到 MainPage.xaml 上的我的全景项目之一。

我不能简单地在我的 WeatherViewModel 中设置 Forecasts.ItemsSource = Forecast;,它表示当前上下文中不存在预测(WeatherView 中的列表框元素名称) .

谁能教我怎么绑定?并且任何人都有一个很好的源/示例示例到 windows-phone 中的 mvvm 模式?之前谢谢。

编辑:

WeatherModel.cs

namespace JendelaBogor.Models
{
    public class WeatherModel
    {
        public string Date { get; set; }
        public string ObservationTime { get; set; }
        public string WeatherIconURL { get; set; }
        public string Temperature { get; set; }
        public string TempMaxC { get; set; }
        public string TempMinC { get; set; }
        public string Humidity { get; set; }
        public string WindSpeedKmph { get; set; }
    }
}

WeatherViewModel.cs

namespace JendelaBogor.ViewModels
{
    public class WeatherViewModel : ViewModelBase
    {
        private string weatherURL = "http://free.worldweatheronline.com/feed/weather.ashx?q=";
        private const string City = "Bogor,Indonesia";
        private const string APIKey = "APIKEY";

        private IList<WeatherModel> _forecasts;
        public IList<WeatherModel> Forecasts
        {
            get 
            {
                if (_forecasts == null)
                {
                    _forecasts = new List<WeatherModel>();
                }

                return _forecasts;
            }

            private set
            {
                _forecasts = value;

                if (value != _forecasts)
                {
                    _forecasts = value;
                    this.NotifyPropertyChanged("Forecasts");
                }
            }
        }

        public WeatherViewModel()
        {
            WebClient downloader = new WebClient();
            Uri uri = new Uri(weatherURL + City + "&num_of_days=5&extra=localObsTime&format=xml&key=" + APIKey, UriKind.Absolute);
            downloader.DownloadStringCompleted += new DownloadStringCompletedEventHandler(ForecastDownloaded);
            downloader.DownloadStringAsync(uri);
        }

        private void ForecastDownloaded(object sender, DownloadStringCompletedEventArgs e)
        {
            if (e.Result == null || e.Error != null)
            {
                MessageBox.Show("Cannot load Weather Forecast!");
            }

            else
            {
                XDocument document = XDocument.Parse(e.Result);
                var current = from query in document.Descendants("current_condition")
                                     select new WeatherModel
                                     {
                                         ObservationTime = DateTime.Parse((string)query.Element("localObsDateTime")).ToString("HH:mm tt"),
                                         Temperature = (string)query.Element("temp_C"),
                                         WeatherIconURL = (string)query.Element("weatherIconUrl"),
                                         Humidity = (string)query.Element("humidity"),
                                         WindSpeedKmph = (string)query.Element("windspeedKmph")
                                     };             

                this.Forecasts = (from query in document.Descendants("weather")
                                       select new WeatherModel
                                       {
                                           Date = DateTime.Parse((string)query.Element("date")).ToString("dddd"),
                                           TempMaxC = (string)query.Element("tempMaxC"),
                                           TempMinC = (string)query.Element("tempMinC"),
                                           WeatherIconURL = (string)query.Element("weatherIconUrl")
                                       }).ToList();
            }
        }
    }
}

WeatherView.xaml

<UserControl x:Class="JendelaBogor.Views.WeatherView"
    xmlns:vm="clr-namespace:JendelaBogor.ViewModels">

    <UserControl.DataContext>
         <vm:WeatherViewModel />
    </UserControl.DataContext>

    <Grid Margin="0,-10,0,0">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <Grid x:Name="Current" Grid.Row="0" Height="150" VerticalAlignment="Top">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="150"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Image Grid.Column="0" delay:LowProfileImageLoader.UriSource="{Binding WeatherIconURL}" Width="120" Height="120" VerticalAlignment="Top"/>
            <StackPanel Grid.Column="1" Height="200" VerticalAlignment="Top">
                <TextBlock Text="{Binding Temperature}" FontSize="22"/>
                <TextBlock Text="{Binding ObservationTime}" FontSize="22"/>
                <TextBlock Text="{Binding Humidity}" FontSize="22"/>
                <TextBlock Text="{Binding Windspeed}" FontSize="22"/>
            </StackPanel>
        </Grid>

        <Grid Grid.Row="1" Height="300"  VerticalAlignment="Bottom" Margin="10,0,0,0">
            <StackPanel VerticalAlignment="Top">
                <StackPanel Height="40" Orientation="Horizontal" Margin="0,0,0,0">
                    <TextBlock Text="Date" FontSize="22" Width="170"/>
                    <TextBlock Text="FC" FontSize="22" Width="60"/>
                    <TextBlock Text="Max" TextAlignment="Right" FontSize="22" Width="90"/>
                    <TextBlock Text="Min" TextAlignment="Right" FontSize="22" Width="90"/>
                </StackPanel>

                <StackPanel Orientation="Horizontal">
                    <ListBox ItemsSource="{Binding Forecasts}">
                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                <StackPanel Height="40" Orientation="Horizontal" Margin="0,10,0,0">
                                    <TextBlock Text="{Binding Date}" FontSize="22" TextAlignment="Left" Width="170" />
                                    <Image delay:LowProfileImageLoader.UriSource="{Binding WeatherIconURL}" Width="40" Height="40" />
                                    <TextBlock Text="{Binding TempMaxC, StringFormat='\{0\} °C'}" TextAlignment="Right" FontSize="22" Width="90" />
                                    <TextBlock Text="{Binding TempMinC, StringFormat='\{0\} °C'}" TextAlignment="Right" FontSize="22" Width="90" />
                                </StackPanel>
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                    </ListBox>
                </StackPanel>
            </StackPanel>
        </Grid>
    </Grid>
</UserControl>

MainPage.xaml

<controls:PanoramaItem x:Name="Weather" Header="weather">
    <views:WeatherView />
</controls:PanoramaItem>

【问题讨论】:

    标签: c# mvvm windows-phone


    【解决方案1】:

    您需要告诉视图您正在使用什么视图模型。通过添加

    <UserControl
        xmlns:vm="clr-namespace:JendelaBogor.ViewModels">
    
        <UserControl.DataContext>
           <vm:WeatherViewModel />
        </UserControl.DataContext>
    
    </UserControl>
    

    所有{Binding} 都映射到WeatherViewModel 类。按照 Reed 的建议,通过在列表框上使用 ItemsSource 属性,您可以绑定通过属性公开的列表中的所有项目。

    如果列表在运行应用程序时发生更改,请考虑使用ObservableCollection 并在收到新数据时将其清除并添加所有新项目。如果你这样做了,你的 GUI 就会随之更新。

    【讨论】:

    • 在我的WeatherView.xaml 中添加&lt;vm:WeatherViewModel /&gt; 后,我的MainPage.xaml 设计窗格显示错误UnauthorizedAccessException: Invalid cross-thread access.。应用程序运行,但Forecasts 未出现。在调试器中,它显示绑定错误,但不是Forecasts。错误参考Current天气数据绑定。我仍在寻找我的代码有什么问题:(
    • 如果您的代码中有任何与 UI 相关的更改,那么您可以使用 Dispatcher.Invoke(new Action(()=&gt;{ })); 包装该代码
    • 另外,您可以使用DesignerProperties.GetIsInDesignMode(DependencyObject)方法来判断一个类是否是设计者创建的,在这种情况下您可以避免从文件或套接字中读取,否则会导致异常。
    • Forecasts 数据绑定有什么想法吗?我仍然无法在WeatherView.xaml 上展示它。完整的代码可以在这里找到:https://github.com/yudayyy/JendelaBogor/tree/master/JendelaBogor 无论如何,感谢您到目前为止帮助我。
    • 为什么绑定不了?你有例外吗?
    【解决方案2】:

    ViewModel 不知道视图。

    您需要在 ViewModel 上创建一个 Forecasts 属性,并将 ItemsSource 从您的视图绑定到它。在您看来,将ListBox 更改为:

    <!-- No need for a name - just add the binding -->
    <ListBox ItemsSource="{Binding Forecasts}">
    

    然后,在您的 ViewModel 中,添加:

    // Add a backing field
    private IList<WeatherModel> forecasts;
    
    // Add a property implementing INPC
    public IList<WeatherModel> Forecasts 
    { 
        get { return forecasts; }
        private set
        {
            forecasts = value;
            this.RaisePropertyChanged("Forecasts");
        }
    }
    

    然后你可以在你的方法中设置它:

     this.Forecasts = (from query in document.Descendants("weather")
                                 select new WeatherModel
                                 {
                                     Date = DateTime.Parse((string)query.Element("date")).ToString("dddd"),
                                     TempMaxC = (string)query.Element("tempMaxC"),
                                     TempMinC = (string)query.Element("tempMinC"),
                                     WeatherIconURL = (string)query.Element("weatherIconUrl")
                                 })
                     .ToList(); // Turn this into a List<T>
    

    【讨论】:

    • 绑定元素(勾选),做ViewModel(勾选,RaisePropertyChanged -> NotifyPropertyChanged),设置方法(勾选),模拟器上依然没有出现数据。我在这里错过了什么吗?我应该发布我的 ViewModelBase @Reed 吗?
    • @yudayyy 您是否遇到绑定错误(检查模拟器中的输出窗口)?您的列表设置是否正确(检查调试器)?
    • @Patrick 是的,我也这么认为,然后在WeatherView.xaml.cs(自定义用户控件)中尝试this.DataContext = new WeatherViewModel();,但仍然没有运气。如何设置DataContext?
    • 亲爱的 ReedCopsey,你能帮我解决这个问题吗i.stack.imgur.com/tP76m.png 列表已被填充,但仍未出现在我的应用程序中。正如 Patrick 所说,我什至添加了 DataContext。
    猜你喜欢
    • 2013-02-20
    • 2013-11-21
    • 2013-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-25
    • 2012-09-29
    • 1970-01-01
    相关资源
    最近更新 更多