简体   繁体   中英

How to bind ItemsSource to ObservableCollection Property of the parent Window?

I know this has been asked times before but none of the solutions so far works for me. I try to bing an ObservableCollection to the ItemsSource propery of a combobox but fail miserably.

The ObservalbeCollection is part of the Window.cs:

public partial class RecordSoundWindow2 : Window
{
    public ObservableCollection<string> RecordingDevices { get; set; }

    public RecordSoundWindow2()
    {
        InitializeComponent();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        RecordingDevices = new ObservableCollection<string>();

        for (int n = 0; n < WaveIn.DeviceCount; n++)
        {
            RecordingDevices.Add(WaveIn.GetCapabilities(n).ProductName);
        }
    }

I certainly know that it gets updated with my microphone name, but the combobox never shows its contents. I've tried the following approaches so far:

<ComboBox DockPanel.Dock="Top" Name="cbDevices" ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=RecordingDevices}"/>
<ComboBox DockPanel.Dock="Top" Name="cbDevices" ItemsSource="{Binding RelativeSource={RelativeSource Self}, Path=RecordingDevices}"/>
<ComboBox DockPanel.Dock="Top" Name="cbDevices" ItemsSource="{Binding Path=RecordingDevices}"/>

I've seen many answers where the RealtiveSource with FindAncestor works, but not for me. What am I missing here?

You need to change your property to a Dependency Property . Your bindings will not work unless you set the DataContext .

In your case, your are inheriting from a Window , therefore a DependencyProperty is the correct way to go.

public ObservableCollection<string> RecordingDevices
    {
        get { return (ObservableCollection<string>)GetValue(RecordingDevicesProperty); }
        set { SetValue(RecordingDevicesProperty, value); }
    }

    // Using a DependencyProperty as the backing store for RecordingDevices.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty RecordingDevicesProperty =
        DependencyProperty.Register("RecordingDevices", typeof(ObservableCollection<string>), typeof(RecordSoundWindow2), new PropertyMetadata(null));

Mike Eason's answer works.

However Clemens' INotifyPropertyChanged approach is cleaner and I prefer it so I am posting this as answer.

This is the XAML:

<ComboBox Name="cbDevices" Width="200" 
    SelectionChanged="cbDevices_SelectionChanged"
    ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=RecordingDevices}"/>

Following is the code behind with a solution I found on another answer for a better INotifyPropertyChanged approach where the Property name is not given as string, allowing for better renaming of variables afterwards:

public partial class RecordSoundWindow : Window, INotifyPropertyChanged
{
    private IAudioRecorder Recorder;

    public ObservableCollection<string> RecordingDevices { get; set; }

    public RecordSoundWindow()
    {
        InitializeComponent();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        InitRecorder();
    }

    private void InitRecorder()
    {
        RecordingDevices = new ObservableCollection<string>();
        Recorder = new AudioRecorder();

        for (int n = 0; n < WaveIn.DeviceCount; n++)
        {
            RecordingDevices.Add(WaveIn.GetCapabilities(n).ProductName);
            OnMyPropertyChanged(() => RecordingDevices);
        }

        //cbDevices.ItemsSource = RecordingDevices;

        if (RecordingDevices.Count > 0)
            cbDevices.SelectedIndex = 0;

        Recorder.SampleAggregator.MaximumCalculated += OnRecorderMaximumCalculated;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    // Raise the event that a property has changed in order to update the visual elements bound to it
    internal void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
    //Converts the passed parameter to its name in string
    internal void OnMyPropertyChanged<T>(Expression<Func<T>> memberExpression)
    {
        MemberExpression expressionBody = (MemberExpression)memberExpression.Body;
        OnPropertyChanged(expressionBody.Member.Name);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM