简体   繁体   中英

c# How to get Listbox selection from Observable Collection

I'm probably not even asking this correctly, I am new to c#, but trying to help my 14 year-old son learn. I've created a listbox with items created with an ObservableCollection. Here is the XAML:

<ListBox x:Name="listBox1" ItemsSource="{Binding}" Margin="105,205,886,63"
   IsTabStop="True" SelectionChanged="PrintText" 
   ScrollViewer.VerticalScrollBarVisibility="Hidden" TabIndex="5" FontSize="36" 
   Background="Transparent" Foreground="#FF55B64C" FontFamily="Arabic Typesetting" 
   FontWeight="Bold" IsDoubleTapEnabled="False" SelectionMode="Single" >
     <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Name="blockNameList" Text="{Binding name}"/>
                <TextBlock Text=" #"/>
                <TextBlock Name="blockIdList" Text="{Binding id}"/>
            </StackPanel>
        </DataTemplate>
     </ListBox.ItemTemplate> 
</ListBox>

Here is how I created the ListBox Items:

var client = new HttpClient();
var uri = new Uri("http://theurlImusing");
Stream respStream2 = await client.GetStreamAsync(uri);
// DataContractJsonSerializer ser2 = new DataContractJsonSerializer(typeof(RootObject));
// RootObject feed2 = (RootObject)ser2.ReadObject(respStream2);
DataContractJsonSerializer ser = null;
ser = new DataContractJsonSerializer(typeof(ObservableCollection<RootObject>));
ObservableCollection<RootObject> feed2 = ser.ReadObject(respStream2) as ObservableCollection<RootObject>;

var cardList = new List<RootObject>();

foreach (RootObject returnfeed in feed2)
{
    string cid = returnfeed.id;
    string cardname = returnfeed.name;
    listBox1.Items.Add(new RootObject { id=cid, name=cardname });
}

I thought I would just use the SelectionChanged="PrintText" property of the listbox so that when I clicked on a listbox item, it would just change a textblock's text value. Ultimately, that is all I am trying to do...set a textblock or textbox to be equal to the "id" value that is clicked on in the ListBox.

void PrintText(object sender, SelectionChangedEventArgs args)
{
     //What do I put in here??    
}

Thanks very much for any insight! I need it!!

This is something that is much easier to do using data binding. You can bind the TextBlock.Text property directly to the ListBox using an ElementName binding:

<TextBox Text="{Binding ElementName=listBox1,Path=SelectedItem.id}" />

Alternatively, if you set set SelectedValuePath="id" on the ListBox, then binding to SelectedValue will give you the "id" property:

<ListBox x:Name="listBox1" SelectedValuePath="id" ... />
<TextBox Text="{Binding ElementName=listBox1,Path=SelectedValue}" />

As a side note (as @Rachel already noted in comments): you may as well just set the ItemsSource , rather than looping through and adding each manually. All you need is this:

listBox1.ItemsSource = feed2;

Edit

Ok, if you wanted to use the procedural approach, here's how you would do it. (No one would recommend this approach, especially if you're learning/teaching. Try to make full use of data binding, and view-viewmodel separation.)

void PrintText(object sender, SelectionChangedEventArgs args)
{
    var listBox = (ListBox)sender;
    RootObject selectedItem = listBox.SelectedItem;
    someTextBox.Text = selectedItem.id;
}

If all you want to do is click an item in the ListBox and get it to show up in the TextBox, you don't need fancy binding (in that other answer) to do it. You can simply add a MouseUp event in the ListBox XAML:

MouseUp="ListBox1_MouseUp"

This would work similar to the SelectionChanged event you wanted to use.

You then right-click that function name in the XAML page and select "Go to definition". It will create the next function for you:

private void ListBox1_MouseUp(object sender, MouseButtonEventArgs e)
{
}

Simply add in there to update the TextBox you want with the SelectedItem values from sender :

private void ListBox1_MouseUp(object sender, MouseButtonEventArgs e)
{
    ListBox lstBox = (ListBox)sender;
    ListBoxItem item = lstBox.SelectedItem;

    if (item != null)  // avoids exception when an empty line is clicked
    {
        someBox.Text = item.name;
        someOtherBox.Text = item.id;
    }
}

I later found that blockNameList and blockIdList are not accessible via intellisense because they are within the DataTemplate of the ListBox, so I put someBox and someOtherBox , as references to other TextBoxes you would have to add to the XAML, outside of the ListBox. You would not re-write data inside the ListBox on the same item by clicking it. Even if you could reach the template's TextBlock to do it, you'd just be re-writing that same item with its own values, since it would be the SelectedItem !

Even though there are those that don't recommend this approach because they like binding everything - and in some cases you want binding to occur so that controls on the page update as a result of dependencies (ie do one thing to cause another), I find that manual methods of clicking a button/item/control to update something are just fine and avoid all the model/MVVM BS that has taken over WPF and over-complicated it.

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