1

I am developing my first WP7.1 App using MVVM pattern. (and this is my first question on stackoverflow!)

In one of the screens, I collect payment information like Payment Description(Textbox), Payment Mode/Type(Toolkit:ListPicker), Payment Due Date (Toolkit:DatePicker) etc. with an "Add Info" button. This is wrapped in a ListBox.

<ListBox x:Name="AddPayListBox" ItemsSource="{Binding NewPaymentsInfo}" SelectedItem="{Binding NewPayInfo}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBox x:Name="DescInput" Text="{Binding PayDesc, Mode=TwoWay}"/>
                    <toolkit:ListPicker x:Name="TypeInput" Header="Mode:" CacheMode="BitmapCache"
                            ItemsSource="{Binding DataContext.PayTypesList}"
                            SelectedItem="{Binding DataContext.SelectedPayTypesList, Mode=TwoWay}">
                        <toolkit:ListPicker.ItemTemplate>
                            <DataTemplate>
                                 <TextBlock Text="{Binding Path=PayTypesList.PayTypeDesc}"/>
                            </DataTemplate>
                        </toolkit:ListPicker.ItemTemplate>
                    </toolkit:ListPicker>
                    <toolkit:DatePicker x:Name="DateInput" Value="{Binding DueDate, Mode=TwoWay}"/>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
        <Button x:Name="btnAddPay" Content="Add" Command="{Binding AddCommand}" />
    </StackPanel>

My Model (Implements INotifyPropertyChanged) has a class PaymentInfo that maps to this screen. My ViewModel has an ObservableCollection<PaymentInfo> NewPaymentsInfo property.

Screen's DataContext is set to the VM and ListBox Binding is set to the NewPaymentsInfo collection with SelectedItem to another PaymentInfo object NewPayInfo.

The ListPicker gets its data (Payment Modes like Cash, Card etc.) from another class PayType in Model, which is filled in the VM Constructor.

My problem is that the ListPicker remains empty in the screen. If I use <ListPickerItem> to populate the ListPicker then it works.(This is an workaround since the Payment modes are predefined; however, I would like to get the data of the ItemSource at runtime).

I searched a lot but can't get the Bindings of the ListPicker right. I tried with RelativeSource but no luck. Your expert help is much needed. Please let me know if any more info is needed.

Thank you!


UPDATE:

Here's the Model:

    public class PaymentInfo : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    private string _PayDesc;
    public string PayDesc
    {
        get { return _PayDesc; }
        set
        {
            _PayDesc = value;
            RaisePropertyChanged("PayDesc");
        }
    }

    private byte _PayType;
    public byte PayType
    {
        get { return _PayType; }
        set
        {
            _PayType = value;
            RaisePropertyChanged("PayType");
        }
    }

    private DateTime _DueDate;
    public DateTime DueDate
    {
        get { return _DueDate; }
        set
        {
            _DueDate = value;
            RaisePropertyChanged("DueDate");
        }
    }
}

public class SupportedPayTypes : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    private byte _PayTypeIdx;
    public byte PayTypeIdx
    {
        get { return _PayTypeIdx; }
        set
        {
            _PayTypeIdx = value;
            RaisePropertyChanged("PayTypeIdx");
        }
    }

    private string _PayTypeDesc;
    public string PayTypeDesc
    {
        get { return _PayTypeDesc; }
        set
        {
            _PayTypeDesc = value;
            RaisePropertyChanged("PayTypeDesc");
        }
    }
}

Here's the ViewModel: (not including AddCommand as that part is working fine)

    public class PaymentViewModel
{
    public ObservableCollection<PaymentInfo> NewPaymentsInfo { get; set; }
    public PaymentInfo NewPayInfo;

    public ICommand AddCommand { get; set; }

    public ObservableCollection<SupportedPayTypes> PayTypesList;
    public SupportedPayTypes SelectedPayTypesList;

    public PaymentViewModel()
    {
        AddCommand = new DelegateCommand<PaymentSetup>(AddAction);

        PayTypesList = new ObservableCollection<SupportedPayTypes>();
        PayTypesList.Add(new SupportedPayTypes() { PayTypeIdx = 0, PayTypeDesc = "Cash" });
        PayTypesList.Add(new SupportedPayTypes() { PayTypeIdx = 1, PayTypeDesc = "Credit Card" });

        //SelectedPayTypesList = PayTypesList[0];
        SelectedPayTypesList = new SupportedPayTypes();
        SelectedPayTypesList.PayTypeIdx = PayTypesList[0].PayTypeIdx;
        SelectedPayTypesList.PayTypeDesc = PayTypesList[0].PayTypeDesc;

        NewPaymentsInfo = new ObservableCollection<PaymentInfo>();
        NewPayInfo = new PaymentInfo();
        NewPayInfo.PayDesc = "";
        NewPayInfo.PayType = SelectedPayTypesList.PayTypeIdx;
        NewPayInfo.DueDate = DateTime.Now;
        NewPaymentsInfo.Add(NewPayInfo);
    }
}

Finally, here's the MainPage.xaml.cs N.B. I am using Pivot MainPivot and the screen in question is AddView

public partial class MainPage : PhoneApplicationPage
{
    private PaymentViewModel vm;

    public MainPage()
    {
        InitializeComponent();
        vm = new PaymentViewModel();
    }

    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
    {
        base.OnNavigatedTo(e);

        AddView.DataContext = vm;
        MainPivot.SelectedIndex = 1;
    }
}

Please let me know if you find anything missing? I have already reached the end of my wits :)

Thank You!

NP3
  • 652
  • 8
  • 21
  • Welcome to SO! Great first question. Are you instantiating NewPaymentsInfo in the constructor of your ViewModel? If not, you will need to fire the property change notification on it after it's been instantiated. You can post the code for your Model/ViewModel if you need a further look at it :) – ChrisO Oct 19 '13 at 23:11
  • @ChrisO - Thanks! Yes I am instantiating `NewPaymentsInfo` in the VM constructor. I will post the Model/VM code shortly and further try to explain more. – NP3 Oct 20 '13 at 04:33

1 Answers1

0

Right, so you were correct in assuming that you need a RelativeSource binding to get to your PayTypesList collection. RelativeSource allows you to walk up the visual tree and bind to any parent element. In this case, I'd recommend that you walk up to the ListBox and then use its DataContext to access the PaymentViewModel.

Do yourself a favour and download Snoop which will allow you to view the visual tree of your application at run time and helps in selecting an appropriate target element for the binding.

The ItemsSource binding on your ListPicker becomes this:

ItemsSource="{Binding Path=DataContext.PayTypesList, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}"

This is telling the binding to keep traversing up the visual tree until it finds the first UIElement of type ListBox and then to use this as the target of the binding. You'll of course want a similar binding for your SelectedItem using the SelectedPayTypesList property.

Edit: By the way, I think you'll want the SelectedPayType to be a member of the PaymentInfo model rather than the PaymentViewModel. I assume you want each item in the ListBox to have its own selection!

ChrisO
  • 5,068
  • 1
  • 34
  • 47
  • Well I tried this but I get AncestorType not supported error. It seems that in Silverlight and WP it is not supported! Have a look at [here](http://stackoverflow.com/questions/15233072/windowsphone-relativesource-mode-findancestor-ancestortype-cannotresolve-sy) – NP3 Oct 21 '13 at 15:41
  • Ahh, in that case, this one might help http://stackoverflow.com/questions/2291310/silverlight-4-relativesource-findancestor-binding/8564277#8564277 it seems to have a few workarounds. Sorry I can't be much help, I use WPF and not Silverlight. – ChrisO Oct 21 '13 at 16:07
  • Thanks ChrisO for your valuable suggestions. I will take a look at your links and post an update tomorrow. – NP3 Oct 21 '13 at 17:37
  • I went through the link (and sub-links) provided by ChrisO. I implemented various solutions provided but at least for WP7 none seem to be working. I am back to square one, at least for now :( I thought the titled scenario is common but it seems it is not supported in WP7?!? – NP3 Oct 22 '13 at 13:16