How to send an object of another class to itself? - c#

I am very new to WPF and couldn't get my head around this so checked couple tutorials and kinda merged them together and I am here with this mess. Dont really know how should I describe my issue around so I decided to write here.
What I am working on;
Using a WPF I got a menu opening UserControls inside the Main Window.
When Main Window starts I am starting a thread like this so I get a loop
private void MainLoop()
{
Thread th = Thread.CurrentThread;
while (th.ThreadState != ThreadState.AbortRequested &&
th.ThreadState != ThreadState.Aborted)
{
if (bMainOk)
{
switch (activeUC)
{
case "ucDurum":
LoopControl.ucDurumLoop(plc, connectionString);
break;
case "ucAyarlar":
//LoopControl.ucAyarlarLoop();
break;
default:
break;
}
}
Thread.Sleep(2000);
}
}
That LoopControl class has functions inside that are looping depending on the active UserControl.
public class LoopControl : ObservableObject
{
public static void ucDurumLoop(ActUtlType plc, string connectionString)
{
DurumVM durum = new DurumVM();
// The values do change before this lines, I didnt put them for the sake of clarity
durum.RaporAktif = "true";
durum.RaporAdedi = arrayData[0].ToString();
durum.BufferPercent = "%" + (float)arrayData[0] / 250 * 100;
durum.PlcSonCounter = arrayData[4].ToString();
durum.SqlSonCounter = sonCntr.ToString();
}
}
Which is Connected to a Main ViewModel then sub ViewModels like so
class MainVM
{
public DurumVM Durum { get; private set; }
public AyarlarVM Ayarlar { get; private set; }
public MainVM()
{
Durum = new DurumVM();
Ayarlar = new AyarlarVM();
}
}
public class DurumVM : ObservableObject
{
#region PLCVars
private bool _raporAktif;
private string _raporAdedi;
private string _bufferPercent;
private string _plcSonCounter;
private string _sqlSonCounter;
#endregion
#region PLCGetSets
public string RaporAktif
{
get
{
if (_raporAktif)
return "Düzenlenecek True";
return "Düzenlenecek False";
}
set
{
_raporAktif = Convert.ToBoolean(value);
OnPropertyChanged("RaporAktif");
}
}
public string RaporAdedi
{
get
{
if (string.IsNullOrEmpty(_raporAdedi))
return "Zeroh";
return _raporAdedi;
}
set
{
_raporAdedi = value;
OnPropertyChanged("RaporAdedi");
}
}
public string BufferPercent
{
get
{
if (string.IsNullOrEmpty(_bufferPercent))
return "%0";
return _bufferPercent;
}
set
{
_bufferPercent = (value.Length <= 5 ? value : value.Substring(0, 5));
OnPropertyChanged("BufferPercent");
}
}
public string PlcSonCounter
{
get
{
if (string.IsNullOrEmpty(_plcSonCounter))
return "0";
return _plcSonCounter;
}
set
{
_plcSonCounter = value;
OnPropertyChanged("PlcSonCounter");
}
}
public string SqlSonCounter
{
get
{
if (string.IsNullOrEmpty(_sqlSonCounter))
return "0";
return _sqlSonCounter;
}
set
{
_sqlSonCounter = value;
OnPropertyChanged("SqlSonCounter");
}
}
#endregion
}
And the user control are bound to these values:
<StackPanel>
<DockPanel>
<Label Content="Aktif mi? :" VerticalAlignment="Center"/>
<TextBlock Text="{Binding RaporAktif, UpdateSourceTrigger=PropertyChanged}" DockPanel.Dock="Right" Margin="0,0,25,0" HorizontalAlignment="Right" VerticalAlignment="Center">
<TextBlock.Effect>
<DropShadowEffect/>
</TextBlock.Effect>
</TextBlock>
</DockPanel>
...
public partial class ucDurum : UserControl
{
MainVM viewModel = new MainVM();
//LoopControl viewModel = new LoopControl();
public ucDurum()
{
InitializeComponent();
this.DataContext = viewModel.Durum;
Control.activeUC = "ucDurum";
}
}
Quick sum:
I got set of user controls and one main loop. Main loop has parts that only works if correct user control is active, connects to somewhere else and gets values then sets these values on a reference, sends it to user control and textblocks the values bound will update
What is wrong that I know:
Practice might be very incorrect. It was looking good in my head but I realized the problem. Mainly due to my lack of C# knowledge. The main loop gets reference of VM sets the datas but never sends them anywhere as I bolded in quick summary. I certain the problem is there since the values I got are correct, when I change values view button etc they do change and update. But VM has no idea about LoopControl.
In MVC I'd return the referenced object then get values on view. In here the values are bound directly and I am kind of lost.
Thanks in advance.
Update:
I'm sorry I wasn't clear enough above the last paragraphs.
What I expect it to do:
LoopControl.ucDurumLoop updates DurumVM using referenced object so the user control "ucDurum" can see it and update TextBlock values accordingly.
What is happening:
I set up the referenced object in LoopControl.ucDurumLoop with the values but that is just an object and stays there. Doesn't go to DurumVM and update values using their Get/Set 's, so values are same and user control "ucDurum" has no idea what is going on on LoopControl.

Well I was being stupid.
Problem was, I was creating new object reference and updating it while I had to reference the one on UserControl.
All I needed was changing a line in LoopControl
public class LoopControl : ObservableObject
{
public static void ucDurumLoop(ActUtlType plc, string connectionString)
{
DurumVM durum = new DurumVM(); // Changing this
DurumVM durum = ucDurum.viewModel; // To this.
durum.RaporAktif = "true";
durum.RaporAdedi = arrayData[0].ToString();
durum.BufferPercent = "%" + (float)arrayData[0] / 250 * 100;
durum.PlcSonCounter = arrayData[4].ToString();
durum.SqlSonCounter = sonCntr.ToString();
}
}

Related

Binding two textBoxes using MVVM Light

I have two text boxes side by side, InputInches and InputMillimeters what I want to do is inches to millimeters conversions similar to the way Goolge conversion works. What I want to happen is that when the user starts typing in the first text box InputInches the result would be shown in the second text box (InputMillimeters) and vice-versa, if the user starts typing in the second text box the result would be shown in the first text box.
The following code works fine to do inches to millimeters conversion exactly as I want but if I un-comment the code in the convertMillimetersToInches() method I get an error.
Any suggestion on how can I do this type of binding using MVVM Light?
UI:
XAML:
<TextBox x:Name="textBox1"
Text="{Binding InputInches,UpdateSourceTrigger=PropertyChanged}"/>
<TextBox x:Name="textBox2"
Text="{Binding InputMillimeters,UpdateSourceTrigger=PropertyChanged}"/>
ViewModel:
namespace MyApp.ViewModel
{
public class ConversionViewModel : ViewModelBase
{
private string _inputInches;
private string _inputInchesTrimmed;
private string _inputMillimeters;
private string _inputMillimetersTrimmed;
public ConversionViewModel()
{
}
public string InputInches
{
get { return _inputInches; }
set {
_inputInches = value;
_inputInchesTrimmed = value.Trim();
RaisePropertyChanged();
if (_inputInchesTrimmed == "") {
_inputInchesTrimmed = "0";
}
convertInchesToMillimeters();
}
}
public string InputMillimeters
{
get { return _inputMillimeters; }
set {
_inputMillimeters = value;
_inputMillimetersTrimmed = value.Trim();
RaisePropertyChanged();
if (_inputMillimetersTrimmed == "") {
_inputMillimetersTrimmed = "0";
}
convertMillimetersToInches();
}
}
/// CONVERSION METHODS
private void convertInchesToMillimeters()
{
double millimeters = Convert.ToDouble(_inputInchesTrimmed) * 25.4;
InputMillimeters = Convert.ToString(millimeters);
}
private void convertMillimetersToInches()
{
//double inches = Convert.ToDouble(_inputInchesTrimmed) / 25.4;
//InputInches = Convert.ToString(inches);
}
}
}
ERROR MESSAGE:
Make sure you don't have an infinite loop or inifinite recursion
Simple answer :
Check on your methods the equality before set value (duplicate for other method) :
private void convertInchesToMillimeters()
{
string millimeters = (Convert.ToDouble(_inputInchesTrimmed) * 25.4).ToString();
if(millimeters != InputMillimeters) InputMillimeters = millimeters;
}
More complex answer. Use just one property and implement two wpf converter(see https://www.wpf-tutorial.com/data-binding/value-conversion-with-ivalueconverter/)

MVVM pattern: an intermediate View between Command binding and ViewModel execute

Scenario
Some date are loaded into a program (e.g., evaluation of students in a class where each student is a distinct entity with his/her evaluation data) and a summary of them is shown on a datagrid. The user selects selects some of the students, and performs an analysis on their evaluation. The analysis process requires some parameters, therefore before analysis a window pops-up and lets user to specify his preferred parameters; then the analysis process executes.
Implementation summary
The datagrid is defined as following and binded to a ViewModel:
<DataGrid x:Name="CachedSamplesDG" ItemsSource="{Binding cachedDataSummary}">
<DataGrid.Columns>
<DataGridTextColumn Header="name" Binding="{Binding name}"/>
<DataGridTextColumn Header="score" Binding="{Binding score}"/>
</DataGrid.Columns>
</DataGrid>
The button that starts the process is defined as following:
<Button x:Name="AnalysisBT" Content="Analyze" Command="{Binding AnalyzeCommand}" CommandParameter="{Binding ElementName=CachedSamplesDG, Path=SelectedItems}"/>
The ViewModel is pretty basic and summarized as following:
internal class CachedDataSummaryViewModel
{
public CachedDataSummaryViewModel()
{
_cachedDataSummary = new ObservableCollection<CachedDataSummary>();
AnalyzeCommand = new SamplesAnalyzeCommand(this);
}
private ObservableCollection<CachedDataSummary> _cachedDataSummary;
public ObservableCollection<CachedDataSummary> cachedDataSummary { get { return _cachedDataSummary; } }
public ICommand AnalyzeCommand { get; private set; }
}
And here is the definition of analysis command:
internal class SamplesAnalyzeCommand : ICommand
{
public SamplesAnalyzeCommand(CachedDataSummaryViewModel viewModel)
{
_viewModel = viewModel;
}
private CachedDataSummaryViewModel _viewModel;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
// canExecute logic
}
public void Execute(object parameter)
{
// process mess ...
// Here I need the selected rows of datagird, which "parameter" delegates them.
// I also need some other parameters for analysis which user can set through another view
}
}
An this is a diagram of my current process and what I would like to do next
Question
When the button is clicked
Apply some UI changes on MainWindow
Pop-up ProcessOptionsWindow
Get set parameters from ProcessOptionsWindow
Pass the selected datagrid rows and user specified parameters to SamplesAnalyzeCommand
What would be the best way to achieve this requirement ?
simply use a dialogservice like Good or bad practice for Dialogs in wpf with MVVM?.
then you can do something like this in your ViewModel
var result = this.uiDialogService.ShowDialog("Prozess Options Window", prozessOptionVM);
...
var parameter1 = prozessOptionVM.Parameter1;
You can define another Model and ViewModel for Process Options, and then in the SamplesAnalyzeCommand, display the ProcessOptionsView. When user is done with the ProcessOptionsView, the main ViewModel gets notified (e.g by an event handler) and completes the Process.
Something like this:
internal class SamplesAnalyzeCommand : ICommand {
...
public void Execute(object parameter)
{
this._viewModel.ShowProcessOptions(parameter);
}
}
internal class CachedDataSummaryViewModel {
public string Status {
get {
return this.status;
}
set {
if (!string.Equals(this.status, value)) {
this.status = value;
// Notify property change to UI
}
}
}
...
internal void ShowProcessOptions(object paramter) {
// Model
var processOptions = new ProcessOptionsModel() {
otherInfo = parameter
};
// View-Model
var processOptionsViewModel = new ProcessOptionsViewModel();
processOptionsViewModel.Model = processOptions;
// View
var processOptionsView = new ProcessOptionsView(
processOptionsViewModel
);
// Edit2: Update status
this.Status = "Selecting process options...";
// You can use the event handler or dialog result
processOptionsViewModel.OK += this.PerformProcess;
processOptionsView.ShowDialog();
}
private void PerformProcess(object sender, EventArgs e) {
var processOptionsView = sender as ProcessOptionsView;
var processOptionsModel = processOptionsView.Model;
var processOptions = processOptionsModel.Model;
// Edit2: Update status
this.Status = "Performing process...";
// use processOptions.OtherInfo for initial info
// use processOptions.* for process options info
// and perform the process here
// Edit2: Update status
this.Status = "Process Done.";
}
...
}
class ProcessOptionsModel {
public object OtherInfo {
get;
set;
public int Parameter1 {
get;
set;
}
public IList<ProcessItem> SelectedItems {
get;
set;
}
...
}
class ProcessOptionsViewModel {
public event EventHandler OK;
private SamplesAnalyzeCommand model;
private ICommand okCommand;
public ProcessOptionsViewModel() {
this.okCommand = new OKCommand(this.OnOK);
}
public SamplesAnalyzeCommand Model {
get {
return model;
}
set {
this.model = value;
// Property changed stuff here
}
}
private void OnOK(object parameter) {
if (this.OK != null) {
this.OK = value;
}
}
}
class ProcessOptionsView {
// Interacts with it's view-model and performs OK command if
// user pressed OK or something
}
Hope it helps.
Edit (1):
As blindmeis suggested, you may use some Dialog Service to make the connection between the views.
Edit (2):
Immidiate UI changes after button click can be done in ShowProcessOptions method of the ShowProcessOptions. I don't think you want reflect ui changes of the options window while user works with it, to the main window. UI changes after user closes options window can be done in PerformProcess.
If you want to make an abstraction for options selection (e.g reading from a file) as you mentioned in the comment below, you may define an IOptionsProvider interface, and put ProcessOptionsView and View-Model behind that but still you use the same model.
interface IOptionsProvider {
ProcessOptionsModel GetProcessOptions();
}
class ProcessOptionsView : IOptionsProvider {
public ProcessOptionsModel GetProcessOptions() {
if (this.ShowDialog()) {
return this.ModelView.Model;
}
return null;
}
}
class ProcessOptionsFromFile : IOptionsProvider {
public ProcessOptionsModel GetProcessOptions() {
// Create an instance of ProcessOptionsModel from File
}
}
Note that in this case I removed the OK event since the GetProcessOptions is supposed to block until user closes the main window. If you want a responsive approach in the FromFile case, you may need to work on the async stuff, maybe define GetProcessOptionsAsync instead.
In this case things may get a little bit complicated but I guess it is achievable in this way.

How to handle making a Window in WPF (C#) that takes parameters?

I am using WPF (C#) for the first time and this I've encountered my first "real" design choice. I have a main window and when the user enters some data and presses the "plot" Button, a new window will come up showing a graph.
This graph window I am defining myself with a combination of xaml and the code-behind file. The issue is that 2 parameters this window has is the x axis title and the y axis title. So, these should be "parameters" to making this window.
I am confused by this because I'm using MVVM and I have a "ViewModel" for the window called GraphWindowPresenter and a "View" for the class called GraphWindowView.
At first, I tried to have an xAxis property and a yAxis property in my GraphWindowPresenter but that will not work since I need to "bind" to these values upon construction of the GraphWindowView. Additionally, this approach would require that my GraphWindowPresenter take an xAxis parameter and a yAxis parameter which is problamatic as well since I just create an instance of the class in the xaml of GraphWindowView.
I'm thinking of a possible soltuion that I can just have my GraphWindowView take the xAxis and yAxis parameters but doesn't this violate MVVM? I would rather not do that.
Note: This is similar to this post MVVM: Binding a ViewModel which takes constructor args to a UserControl. But in my scenario it is tricky since I have a parent window and a pop up child window.
Question: What is the best approach to this design issue? What are the "best practices" regarding this scenario?
Possible Answer:
Is this the correct use of dependency properties that you described? Is this a "clean" solution?
private void doGraph()
{
if (log == null) // if a log is not loaded
{
MessageBoxResult mbr = MessageBox.Show("A log file must be " +
"loaded before plotting.",
"Warning",
MessageBoxButton.OK,
MessageBoxImage.Exclamation);
return;
}
// NOW MUST PRESENT GRAPH WINDOW
GraphWindowView gwv = new GraphWindowView();
gwv.xAxis = X_AXIS_VALUE:
gwv.yAxis = Y_AXIS_VALUE;
gwv.Show();
}
And in my GraphWindowView class I have the code:
public partial class GraphWindowView : Window
{
// Using a DependencyProperty as the backing store for yAxis.
public static readonly DependencyProperty yAxisProperty =
DependencyProperty.Register("yAxis", typeof(string), typeof(GraphWindowView));
// Using a DependencyProperty as the backing store for xAxis.
public static readonly DependencyProperty xAxisProperty =
DependencyProperty.Register("xAxis", typeof(string), typeof(GraphWindowView));
public string xAxis
{
get { return (string)GetValue(xAxisProperty); }
set { SetValue(xAxisProperty, value); }
}
public string yAxis
{
get { return (string)GetValue(yAxisProperty); }
set { SetValue(yAxisProperty, value); }
}
public GraphWindowView()
{
InitializeComponent();
}
}
You can you userSetting properties
One my application have same scenario in that i have mainWindow that accept HostAddress,Port value and it will use another window when i click connect so i am using userSetting properties. I am also using MVVM pattern check code snippet below
XAML:
<TextBox Width="120" Canvas.Left="132" Canvas.Top="16" Text="{Binding Path=Server,Mode=TwoWay}"/>
<TextBox Width="120" Canvas.Left="132" Canvas.Top="42" Text="{Binding Path=DisplayPort,Mode=TwoWay}"/>
<TextBox Width="120" Canvas.Left="132" Canvas.Top="69" Text="{Binding Path=CtrlPort,Mode=TwoWay}"/>
<Button Content="Launch" Name="btnLaunch" Command="{Binding Path=appSetting}" Canvas.Left="132" Canvas.Top="100" Width="120" Height="51" Click="btnLaunch_Click" />
VIEWMODE:
public class SettingsViewModel : ViewModelBase
{
private Settings _settings { get; set; }
public SettingsViewModel()
{
appSetting = new RelayCommand(this.AppSettingsCommand);
_settings = ApplicationTest.Properties.Settings.Default;
}
private string _server = Settings.Default.Server;
public string Server
{
get { return this._server; }
set
{
if (this._server != value)
{
this._server = value;
OnPropertyChanged("Server");
}
}
}
private string _displayPort = Settings.Default.DisplayPort;
public string DisplayPort
{
get { return this._displayPort; }
set
{
if (this._displayPort != value)
{
this._displayPort = value;
OnPropertyChanged("DisplayPort");
}
}
}
private string _ctrlPort = Settings.Default.CtrlPort;
public string CtrlPort
{
get { return this._ctrlPort; }
set
{
if (this._ctrlPort != value)
{
this._ctrlPort = value;
OnPropertyChanged("DisplayPort");
}
}
}
public RelayCommand appSetting
{
get;
set;
}
private void AppSettingsCommand()
{
this._settings.Server = this.Server;
this._settings.DisplayPort = this.DisplayPort;
this._settings.CtrlPort = this.CtrlPort;
this._settings.Save();
}

How to bind a combo-box to a collection of multi-language values in WPF?

I am trying to set up a multi-language application, so when the user changes the display language all the texts in all the open windows change automatically.
I am having issues through with binding combo-box control. The binding needs to be done in code-behind as I have dynamic content coming from a database, and sometimes I even have to create additional combo-boxes at runtime.
Also I do not want to keep the translations in the database because I do not want to query the database every time a user is changing the display language.
What I did until now:
in xaml:
<ComboBox x:Name="cmb"/>
and in C#:
public class MyCmbItem
{
public int Index { get; set; }
public string Text { get; set; }
}
private ObservableCollection<MyCmbItem> LoadText()
{
ObservableCollection<MyCmbItem> _result = new ObservableCollection<MyCmbItem>();
foreach (var _item in _list)
{
//the list is coming from a database read
_result.Add(new MyCmbItem { Index = _item.Value, Text = _res_man_global.GetString(_item.KeyText, _culture) });
}
return _result;
}
public ObservableCollection<MyCmbItem> MyTexts
{
get { return LoadText(); }
set {} //I do not have to add/remove items at runtime so for now I leave this empty
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
...
LoadList(); //this adds values in _list
cmb.ItemsSource = MyTexts; //this populates the combo-box
Here I got stuck and I do not know how to determine the combo-box to refresh the displayed texts. The method must achieve that if I have several windows opened each containing a random number of combo-boxes, when I change the current language all the combo-boxes in all the windows will refresh the displayed list, without affecting other values inside (like the selected item). Does anybody know how this can be done?
Many thanks.
For your xaml UI, the INotifyPropertyChanged interface indicates updates of the viewmodel. You can extend your class like this:
public class MyCmbItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string APropertyName)
{
var property_changed = PropertyChanged;
if (property_changed != null)
{
property_changed(this, new PropertyChangedEventArgs(APropertyName));
}
}
private string _Text;
private string _KeyText;
public int Index { get; set; }
public string Text
{
get { return _Text;}
set {
if (_Text != value)
{
_Text = value;
NotifyPropertyChanged("Text");
}
}
}
public MyCmbItem(string key_text, int index)
{
Index = index;
_KeyText = key_text;
RefreshText();
_res_man_global.LanguageChanged += () => RefreshText();
}
public void RefreshText()
{
Text = _res_man_global.GetString(_KeyText, _culture);
}
}
Your view can simply bind to the Text-property as following:
<DataTemplate DataType="{x:Type local:MyCmbItem}">
<TextBlock Text="{Binding Path=Text}"/>
</DataTemplate>
Note: I assumed that your language class is global and has some kind of language-changed notification event.

ListBox ItemsSource Doesn't Update

I am facing a ListBox's ItemsSource related issue. I am implementing MVVM with WPF MVVM toolkit version 0.1.
I set one ListBox itemSource to update when a user double clicks on some other element (I handled the event in the code behind and executed the command there, since binding a command to specific events are not supported). At this point through the execution of the command a new ObservableCollection of items get generated and the ListBox's ItemsSource is intended to get updated accordingly. But it is not happening at the moment. ListBox does not update dynamically. What can be the problem? I am attaching relvent code for your reference.
XAML:
List of items which is doubled click to generate the next list:
<ListBox Height="162" HorizontalAlignment="Left" Margin="10,38,0,0" Name="tablesViewList" VerticalAlignment="Top" Width="144" Background="Transparent" BorderBrush="#20EEE2E2" BorderThickness="5" Foreground="White" ItemsSource="{Binding Path=Tables}" SelectedValue="{Binding TableNameSelected, Mode=OneWayToSource}" MouseDoubleClick="tablesViewList_MouseDoubleClick"/>
Second list of items which currently does not get updated:
<ListBox Height="153" HorizontalAlignment="Left" Margin="10,233,0,0" Name="columnList" VerticalAlignment="Top" Width="144" Background="Transparent" BorderBrush="#20EEE2E2" BorderThickness="5" Foreground="White" ItemsSource="{Binding Path=Columns, Mode=OneWay}" DisplayMemberPath="ColumnDiscriptor"></ListBox>
Code Behind:
private void tablesViewList_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
MainViewModel currentViewModel = (MainViewModel)DataContext;
MessageBox.Show("Before event command is executed");
ICommand command = currentViewModel.PopulateColumns;
command.Execute(null);
MessageBox.Show(currentViewModel.TableNameSelected);
//command.Execute();
}
View Model:
namespace QueryBuilderMVVM.ViewModels
{
//delegate void Del();
public class MainViewModel : ViewModelBase
{
private DelegateCommand exitCommand;
#region Constructor
private ColumnsModel _columns;
public TablesModel Tables { get; set; }
public ControllersModel Operators { get; set; }
public ColumnsModel Columns {
get { return _columns; }
set {
_columns = value;
OnPropertyChanged("Columns");
}
}
public string TableNameSelected{get; set;}
public MainViewModel()
{
Tables = TablesModel.Current;
Operators = ControllersModel.Current;
Columns = ColumnsModel.ListOfColumns;
}
#endregion
public ICommand ExitCommand
{
get
{
if (exitCommand == null)
{
exitCommand = new DelegateCommand(Exit);
}
return exitCommand;
}
}
private void Exit()
{
Application.Current.Shutdown();
}
//Del columnsPopulateDelegate = new MainViewModel().GetColumns;
//Method to be assigned to the delegate
//Creates an object of type ColumnsModel
private void GetColumns() {
ColumnsModel.TableNameParam = TableNameSelected;
Columns = ColumnsModel.ListOfColumns;
}
private ICommand _PopulateColumns;
public ICommand PopulateColumns
{
get {
if (_PopulateColumns == null) {
_PopulateColumns = new DelegateCommand(GetColumns); // an action of type method is passed
}
return _PopulateColumns;
}
}
}
}
Model:
public class ColumnsModel : ObservableCollection<VisualQueryObject>
{
private DataSourceMetaDataRetriever dataSourceTableMetadataObject;// base object to retrieve sql data
private static ColumnsModel listOfColumns = null;
private static object _threadLock = new Object();
private static string tableNameParam = null;
public static string TableNameParam
{
get { return ColumnsModel.tableNameParam; }
set { ColumnsModel.tableNameParam = value; }
}
public static ColumnsModel ListOfColumns
{
get
{
lock (_threadLock)
if (tableNameParam != null)
listOfColumns = new ColumnsModel(tableNameParam);
return listOfColumns;
}
}
public ColumnsModel(string tableName)
{
ColumnsModel.tableNameParam = tableName;
Clear();
try
{
dataSourceTableMetadataObject = new DataSourceMetaDataRetriever();
List<ColumnDescriptionObject> columnsInTable = new List<ColumnDescriptionObject>();
columnsInTable = dataSourceTableMetadataObject.getDataTableSchema("Provider=SQLOLEDB;Data Source=.;Integrated Security=SSPI;Initial Catalog=LogiwizUser", ColumnsModel.tableNameParam);
//List<String> listOfTables = dataSourceTableMetadataObject.getDataBaseSchema("Provider=SQLOLEDB;Data Source=.;Integrated Security=SSPI;Initial Catalog=LogiwizUser");
//List<String> listOfTables = dsm.getDataBaseSchema("G:/mytestexcel.xlsx", true);
//ObservableCollection<VisualQueryObject> columnVisualQueryObjects = new ObservableCollection<VisualQueryObject>();
foreach (ColumnDescriptionObject columnDescription in columnsInTable)
{
VisualQueryObject columnVisual = new VisualQueryObject();
columnVisual.ColumnDiscriptor = columnDescription;
columnVisual.LabelType = "column";
Add(columnVisual);
}
}
catch (QueryBuilderException ex)
{
/* Label exceptionLabel = new Label();
exceptionLabel.Foreground = Brushes.White;
exceptionLabel.Content = ex.ExceptionMessage;
grid1.Children.Add(exceptionLabel);*/
}
}
}
Any help is greatly appreciated. Thanks in advance.
The setter of property Columns should raise a PropertyChanged event.
Implement INotifyPropertyChanged to do so : MSDN INotifyPropertyChanged
I guess MVVM Toolkit provides a way of doing so easily (perhaps ViewModelBase already implement the interface ...).
EDIT : Implementing INotifyPropertyChanged is not enough, you have to raise the event created by INotifyPropertyChanged. You property should look something like this :
private ColumnsModel _columns;
public ColumnsModel Columns
{
get { return _columns; }
set
{
_columns = value;
PropertyChanged("Columns");
}
}
use an observableCollection<T> instead of a List<T>
MSDN DOC:
WPF provides the ObservableCollection class, which is a built-in implementation of a data collection that exposes the INotifyCollectionChanged interface. Note that to fully support transferring data values from source objects to targets, each object in your collection that supports bindable properties must also implement the INotifyPropertyChanged interface. For more information, see Binding Sources Overview.

Categories

Resources