New to Calib, help with binding properties and nested properties

Topics: Getting Started
Nov 21, 2011 at 4:25 PM

Ok, I basically have a ListBox control that binds successfully to an ObservableCollection of domain objects I call TweetFromUser.  This TweetFromUser has 2 properties,

 

   public class TweetFromUser
   {
      public string TweetText { get; set; }
      public User User { get; set; }
   }

 

User is defined as followed:

 

    public class User
    {
        public string Name { get; set; }
        public string ImgAddress { get; set; }
        public string Information { get; set; }
        public string URL { get; set; }
        public string UserID { get; set; }
        public string ScreenName { get; set; }
    }

 

Now, in my MainView.xaml, instead of having everything in the xaml, I created a DatatTemplate in the Resources of the App.xaml that the ListBox uses to build its items.  I read in the documentation that if you have a property of Customer that you want say a textbox to bind to a property within Customer you simply do x:Name="Customer_FirstName"

I'm doing this in my usage, I'm binding the text part of the datatemplate by setting x:Name="TextTweet", then setting the User properties to something like, x:Name="User_ImgAddress" for the Image control.  None of the bindings are working, the listbox fills with empty items.  So I'm not sure what I'm missing.  Here's the App.xaml  portion of the DataTemplate the ListBox uses:

 

           <DataTemplate x:Key="ListBoxItemTemplateMain">
                <Border BorderBrush="White" Margin="3" Padding="3" 
                            BorderThickness="2" CornerRadius="5">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="*" />
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="*" />
                        </Grid.ColumnDefinitions>
                        <Image x:Name="User_ImgAddress" />
                        <TextBlock             
                                    Background="{x:Null}" 
                                    Grid.Row="1" Grid.ColumnSpan="2" x:Name="User_Name" Foreground="White" />
                        <TextBlock                                
                                    Background="{x:Null}" Foreground="White" 
                                    VerticalAlignment="Center"
                                    Width="240"
                                    TextWrapping="Wrap" Grid.Column="1" x:Name="TweetText" />
                    </Grid>
                </Border>
            </DataTemplate>

Here's the ListBox in MainView.xaml:

            <ListBox Height="550"
                     ItemTemplate="{StaticResource ResourceKey=ListBoxItemTemplateMain}"                     
                     Background="{x:Null}" Foreground="White"
                     IsSynchronizedWithCurrentItem="True"
                     Grid.IsSharedSizeScope="True"
                     HorizontalContentAlignment="Stretch"
                     x:Name="Tweets" Width="348"
                     >
            </ListBox>
The binding to Tweets, works, as the listbox fills with empty items but debugging the data is there, just not binding to the view.
 

 

 

Nov 21, 2011 at 6:13 PM
Edited Nov 21, 2011 at 6:13 PM

What does your viewmodel look like for MainView.xaml?  I have a feeling soemthing is missing there for the binding to work properly.

Nov 21, 2011 at 7:40 PM
Edited Nov 21, 2011 at 7:43 PM

 

    public class MainViewModel : PropertyChangedBase, ITwitterViewModel
    {
        private readonly ITwitterRepository _repository;

        public MainViewModel(ITwitterRepository repository)
        {
            _repository = repository;
            _nextTweet = "Write your next tweet here.";
            BuildTweetsFromAllUsers();
        }

        // backing stores
        private string _nextTweet;
        private User _selectedUser;
        private ObservableCollection<TweetFromUser> _tweets = new ObservableCollection<TweetFromUser>();
        private ObservableCollection<TweetFromUser> _tweetsFromSelectedUser = new ObservableCollection<TweetFromUser>();
        private TweetFromUser _selectedTweet;

        // properties the View binds to
        public string NextTweet
        {
            get { return _nextTweet; }
            set
            {
                _nextTweet = value;
                NotifyOfPropertyChange(() => NextTweet);
            }
        }

        public User SelectedUser
        {
            get { return _selectedUser; }
            set
            {
                _selectedUser = value;
                NotifyOfPropertyChange(() => SelectedUser);
            }
        }

        public ObservableCollection<TweetFromUser> Tweets
        {
            get { return _tweets; }
        }

        public ObservableCollection<TweetFromUser> TweetsFromSelectedUser
        {
            get { return _tweetsFromSelectedUser; }
        }

        public TweetFromUser SelectedTweet
        {
            get { return _selectedTweet; }
            set
            {
                _selectedTweet = value;
                BuildTweetsOfSelectedUser();
                NotifyOfPropertyChange(() => SelectedTweet);
            }
        }

        public bool CanSendNextTweet
        {
            get { return !string.IsNullOrWhiteSpace(_nextTweet); }
        }

        public void SendNextTweet()
        {
            _repository.Save(new TweetFromUser { TweetText = _nextTweet});
        }

        void BuildTweetsFromAllUsers()
        {
            Tweets.Clear();
            var tweets = _repository.GetAll();
            foreach (var tweetFromUser in tweets)
            {
                Tweets.Add(tweetFromUser);
            }
        }

        void BuildTweetsOfSelectedUser()
        {
            SelectedUser = _selectedTweet.User;
            TweetsFromSelectedUser.Clear();
            var tweetsFromSelectedUser = _repository.GetTweetsByUserId(_selectedUser.UserID);

            foreach (var tweetFromUser in tweetsFromSelectedUser)
            {
                TweetsFromSelectedUser.Add(tweetFromUser);
            }
        }
    }

I got it to work, but not how I wanted it too, basically in the app.xaml dataemplate section I had to use the conventional binding as I did before and then everything loaded up. Would still like to know if it's
possible to have the controls bind simply by their x:Name which is a great feature.

             <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="*" />
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="*" />
                        </Grid.ColumnDefinitions>
                        <Image Source="{Binding Path=User.ImgAddress}" />
                        <TextBlock             
                                    Background="{x:Null}" 
                                    Grid.Row="1" Grid.ColumnSpan="2" Text="{Binding Path=User.Name}" Foreground="White" />
                        <TextBlock                                
                                    Background="{x:Null}" Foreground="White" 
                                    VerticalAlignment="Center"
                                    Width="240"
                                    TextWrapping="Wrap" Grid.Column="1" Text="{Binding Path=TweetText}" />
                    </Grid>

Nov 22, 2011 at 1:23 AM

Ok i see now..

 <TextBlock Background="{x:Null}" 
                  Grid.Row="1" Grid.ColumnSpan="2" x:Name="User_Name" Foreground="White" />

Your code was based off the assumption that binding would insue.  Unfortunately if you have x:Name="User_Name", you then of course have to have a property in your viewmodel named  User_Name.  So while you have the correct binding and it works now, to get to work with the CM "way" properties would have to be defined and set based on the TweetFromUser.  Sometimes the "CM Way" is just more work to get it to do what you expect. 

One thing you also might want to look at is the BindableCollection<> its part of CM's assembly.  It adds a layer of UI Thread safety.