views:

49

answers:

3

Hi!

I have been searching on how to do this for a very long time, and I have not managed to get a straight answer on the subject, so hopefully one of you StackOverflow users will be able to help me here. I have a WPF ListBox named CategoryList and a SDF database called ProgramsList.sdf (with two tables called CategoryList and ProgramsList). What I wish my program to do is get the category names from the CategoryList table and list them in the ListBox control called CategoryList.

Here's the code that I tried, but it only caused my program to crash.

    SqlConnection myConnection = new SqlConnection("Data Source=" + AppDomain.CurrentDomain.BaseDirectory + "ProgramsList.sdf");
    SqlDataReader myReader = null;

    myConnection.Open();
    CategoryList.Items.Clear();
    SqlDataReader dr = new SqlCommand("SELECT Name FROM CategoryList ORDER BY Name DESC", myConnection).ExecuteReader();

    while (myReader.Read())
    {
        CategoryList.Items.Add(dr.GetInt32(0));
    }
    myConnection.Close();

Can anyone help me? Thanks in advance!

+1  A: 

I'd try something like this:

var myConnection = new SqlConnection("Data Source=" + AppDomain.CurrentDomain.BaseDirectory + "ProgramsList.sdf");
var cmd = new SqlCommand("SELECT Name FROM CategoryList ORDER BY Name DESC", myConnection);

myConnection.Open();
CategoryList.Items.Clear();

var sda = new SqlDataAdapter(cmd);
var ds = new DataSet();
sda.Fill(ds);

CategoryList.ItemsSource = ds.Tables["CategoryList"];

myConnection.Close(); 

Note, that you will need to setup the correct bindings in your CategoryList object, likely via some XAML like this:

<ListBox>
    <ListBox.Resources>
        <DataTemplate x:Key="DataTemplateItem">
            <Grid Height="Auto" Width="Auto">
                <TextBlock x:Name="Name" Text="{Binding Name}" />
            </Grid>
        </DataTemplate>
    </ListBox.Resources>
</ListBox>
Nate Bross
I tried it and it gave me an error under ds.Tables["CategoryList"]. I believe you may have gotten mixed up between datatables and collections.EDIT: It should have been ds.Table["CategoryList"].AsEnumerable(). I'll reply back to see if it works.
xplinux557
Try `ds.Tables[0];`
Nate Bross
Sorry. No matter what I do, it still crashes and gives me an Error 26, which is: Error Locating Server/Instance Specified. Any help?PS: Once again, that should be "ds.Tables[0].AsEnumerable();" :)
xplinux557
Actually, ds.Tables shouldn't need .AsEnumerable() because ".Tables" is for DataSets, right? Why does Visual Studio ask for this conversion?
xplinux557
Sounds like your issue is with the connection string then? Are you sure the calculated path is correct?
Nate Bross
I hope so. Just to be sure, you access the connection string by selecting the sdf in the Database Explorer and looking at the Connection String Property, correct?EDIT: The Connection String property contains only the basic string; are there any more parameters you would recommend to add?
xplinux557
Aha! The SQL validation of ServerVersion (located at 'myConnection.ServerVersion') threw an error: System.InvalidOperationException! The connection string should be correct, quickly examining the memory shows that it points to "C:\\Users\\...\\ProgramsList.sdf", which is indeed the correct directory.
xplinux557
Can you do something simple, like just do a messagebox of `SELECT Count(Name)` just to ensure you are able to connect and read data from the db?
Nate Bross
Hi again! I deleted the code that I wrote and added a DataSet through the Solution Explorer called ProgramsListDataSet.xsd. This allowed me to have the DataSet connect to the database for me, and thus, I managed to condense the code to:ProgramsListDataSet progsdata = new ProgramsListDataSet();CategoryList.Items.Clear();CategoryList.ItemsSource = progsdata.Tables["CategoryList"].AsEnumerable();Now the program doesn't crash, but still nothing loads up in the ListBox. What do you think I am doing wrong?
xplinux557
Do you have the XAML bindings setup correctly? Have you tried @strattonn's solution?
Nate Bross
Yes, I have, and my program started crashing again.
xplinux557
A: 

Perhaps you mean: ....

CategoryList.Items.Add(dr.GetString(0));

....

Kevin Driedger
+2  A: 

A much better way is to bind your list to an object you create. That way you can specify properties for DisplayMemberPath (what you see) and SelectedValuePath (your programs internal value).

Here is your main XAML code. Note than the click method of the button will display the currently selected value of the ComboBox. That is going to make things easy later on. Hopefully this is not overkill but it shows a few principles that make WPF easy.

namespace WPFListBoxSample {

public partial class Window1 : Window

{

    WPFListBoxModel model = new WPFListBoxModel();

    public Window1()
    {
        InitializeComponent();
        this.Loaded += new RoutedEventHandler(Window1_Loaded);
    }

    void Window1_Loaded(object sender, RoutedEventArgs e)
    {
        GetData();
        this.DataContext = model;
    }

    public void GetData()
    {
        //SqlConnection myConnection = new SqlConnection("Data Source=" + AppDomain.CurrentDomain.BaseDirectory + "ProgramsList.sdf");
        SqlConnectionStringBuilder str = new SqlConnectionStringBuilder();
        str.DataSource="192.168.1.27";
        str.InitialCatalog="NorthWnd";
        str.UserID="sa";
        str.Password="xyz";
        SqlConnection myConnection = new SqlConnection(str.ConnectionString);

        SqlDataReader myReader = null;

        myConnection.Open();
        SqlDataReader dr = new SqlCommand("SELECT CategoryId, CategoryName FROM Categories ORDER BY CategoryName DESC", myConnection).ExecuteReader();

        while (dr.Read())
        {
            model.Categories.Add(new Category { Id = dr.GetInt32(0), CategoryName = dr.GetString(1) });
        }
        myConnection.Close();
    }

    private void myButton_Click(object sender, RoutedEventArgs e)
    {
        if (this.myCombo.SelectedValue != null)
            MessageBox.Show("You selected product: " + this.myCombo.SelectedValue);
        else
            MessageBox.Show("No product selected");
    }
}

}

The XAML

    <Grid>
    <StackPanel>
        <ComboBox x:Name="myCombo" ItemsSource="{Binding Categories}" DisplayMemberPath="CategoryName"  SelectedValuePath="Id" />
        <Button x:Name="myButton" Content="Show Product" Click="myButton_Click"/>
    </StackPanel>
</Grid>

Your own object for representing a Category

namespace WPFListBoxSample
{
    public class Category
    {
        public int Id { get; set; }
        public string CategoryName { get; set; }
    }
}

Note the {get; set;}'s

Finally a little bit of glue that makes a lot of things easy is putting all your data in a model and binding to the model. This is the way to work WPF.

using System.Collections.Generic;
namespace WPFListBoxSample
{
    public class WPFListBoxModel
    {
        private IList<Category> _categories;
        public IList<Category> Categories
        {
            get
            {
                if (_categories == null)
                    _categories = new List<Category>();
                return _categories; }
            set { _categories = value; }
        }
    }
}
strattonn