简体   繁体   中英

How to make TwoWay Binding work

I have this problem when I put the Binding Mode to TwoWay the DataGrid won't show. When I leave the Binding Mode as it is on default, the DataGrid will apear as strings , and I cannot find the problem. In XAML I have 3 more button s: Load (that loads the table), Update and Cancel (that cancel all the changes and reloads the DataGrid directly from ObservableCollection .

Here is my XAML DataGrid line

<DataGrid x:Name="dataGrid" AutoGenerateColumns="True" Canvas.Left="10" Canvas.Top="10" AlternatingRowBackground="LightGreen" Height="245" Width="500" ItemsSource="{Binding Userss.GetValues, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" DataContext="{Binding RelativeSource={RelativeSource Self}}"/>

I have a Userss Class where i creat my ObservableCollection where i store the data from my SQLite database .

public class Userss : INotifyPropertyChanged
{
    public static SQLiteConnection m_dd = new SQLiteConnection("Data Source=MyDatabase.sqlite;Version=3;");

    public static ObservableCollection<Userss> userCol = new ObservableCollection<Userss>();
    public int Id { get; set; }
    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            RaisePropertyChanged();
        }
    }

    private Sex _sex;
    public Sex Sex
    {
        get { return _sex; }
        set
        {
            _sex = value;
            RaisePropertyChanged();
        }
    }

    private Stations _station;
    public Stations Station
    {
        get { return _station; }
        set
        {
            _station = value;
            RaisePropertyChanged();
        }
    }

    private Jobs _job;
    public Jobs Job
    {
        get { return _job; }
        set
        {
            _job = value;
            RaisePropertyChanged();
        }
    }

    private DateTime _date;
    public DateTime Date
    {
        get { return _date; }
        set
        {
            _date = value;
            RaisePropertyChanged();
        }
    }

    public static ObservableCollection<Userss> GetValues()
    {
        m_dd.Open();
        string sql = "select * from user";
        userCol.Clear();
        SQLiteCommand cmd = new SQLiteCommand(sql, m_dd);
        SQLiteDataReader reader = cmd.ExecuteReader();
        while (reader.Read())
        {
            string sex1 = reader["sex"].ToString();
            string station1 = reader["station"].ToString();
            string job1 = reader["job"].ToString();
            string data1 = reader["date"].ToString();
            userCol.Add(new Userss()
            {
                Id = Convert.ToInt32(reader["id"]),
                Name = reader["name"].ToString(),
                Sex = (Sex)Enum.Parse(typeof(Sex), sex1),
                Station = (Stations)Enum.Parse(typeof(Stations), station1),
                Job = (Jobs)Enum.Parse(typeof(Jobs), job1),
                Date = Convert.ToDateTime(data1)
            });
        }
        m_dd.Close();
        return userCol;

    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged([CallerMemberName] string caller = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(caller));
        }
    }
}

public enum Sex
{
    Male,
    Female
}
public enum Jobs
{
    Programmer,
    Designer,
    Manager,
    CTO,
    CEO,
}
public enum Stations
{
    Desktop,
    Laptop,
    Tablet
}

}

And here is my implementation for my MainWindow

public partial class MainWindow : Window
{
    public SQLiteConnection m_db = new SQLiteConnection("Data Source=MyDatabase.sqlite;Version=3;");
    SQLiteDataAdapter adap;
    DataSet ds;
    DataTable dt;
    SQLiteCommandBuilder cmdbl;
    string Query;
    public MainWindow()
    {
        InitializeComponent();
    }
    private void LoadButton_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            m_db.Open();
            ObservableCollection<Userss> cUser = Userss.GetValues();
            Query = "Select * from user";
            adap = new SQLiteDataAdapter(Query, m_db);
            ds = new DataSet();
            adap.Fill(ds, "Users");
            dt = ds.Tables[0];
            dataGrid.DataContext = ds.Tables[0].DefaultView;
            dataGrid.ItemsSource = dt.DefaultView;
            m_db.Close();
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
    }

    private void Update_Click(object sender, RoutedEventArgs e)
    {
        if (MessageBox.Show("Are you sure you want to make those changes?", "Please confirm", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
        {
            try
            {
                cmdbl = new SQLiteCommandBuilder(adap);
                adap.Update(ds, "Users");
                ds.Tables[0].AcceptChanges();
                dataGrid.Items.Refresh();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        else
            this.dataGrid.CancelEdit();
    }

    private void CancelClick(object sender, RoutedEventArgs e)
    {
        if (MessageBox.Show("Are you sure you want to cancel those changes?", "Please confirm", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
        {
            dataGrid.ItemsSource = Userss.GetValues();
        }
        else
            this.dataGrid.CancelEdit();
    }
}

}

Btw I work in WPF .

Hope someone can help me. Thanks.

The problem is that you are binding to a function, which doesn't work. You should bind to a public property that contains a list of your models that you want to show in your datagrid. That way you need to move your GetValues call to somewhere else, but you allready have that code in your LoadButton_Click Event-Handler, so I would reuse that.

So the first change would be to change your userCol -Collection to a public property instead of a public field, because bindings only work on public properties, besides you want to raise the PropertyChangedEvent if the reference to the your ObservableCollection changes.

// old code
public static ObservableCollection<Userss> userCol = new ObservableCollection<Userss>();

// new Code
private static ObservableCollection<Userss> userCol = new ObservableCollection<Userss>();

public static ObservableCollection<Userss> UserCol {
   get { return userCol; }
   set { userCol = value; RaisePropertyChanged(); }
}

Your GetValues -Method doesn't need a return type anymore, so just change it to void. Inside, change every call to usercol to the new public property UserCol so that if changes to the List happen, the GUI needs to now about it (via the RaisePropertyChanged eg)

Inside of your LoadButton_Click you are basically doing the same as in your GetValues function which also doesn't make much sense, especially because you don't use your Userss models anymore. You can instead just use the GetValues -Method here to reload all the data (also you would need another event eg DataGridLoaded or something to fill your data at startup):

private void LoadButton_Click(object sender, RoutedEventArgs e)
{
    // ... exception handling
    Userss.GetValues();
}

Now simply bind to your collection in your xaml view:

<DataGrid x:Name="dataGrid" AutoGenerateColumns="True" Canvas.Left="10" Canvas.Top="10" AlternatingRowBackground="LightGreen" Height="245" Width="500" ItemsSource="{Binding Userss.UserCol, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" DataContext="{Binding RelativeSource={RelativeSource Self}}"/>

Note that this is just the general idea and that I have not testet this code, so you maybe need to fix it in a view places. Also I think it would be good if you would look a bit into the MVVM design pattern to structure your code in a nice fashion.

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