I'm new at MVVM and I have a problem with updating lists.
I have 2 windows and ListViews in it. They are connected to a property "Tasks". When I add a new row to my db I need to refresh ListViews. I've done it, but only for 1 window.
adding a new row to a db table
private void OnAddTaskExecuted(object p)
{
tasks tsk = new tasks()
{
taskname = "1",
description = "",
date = DateTime.Now,
empID = 2
};
Core.db.tasks.Add(tsk);
Core.db.SaveChanges();
Tasks = new ObservableCollection<tasks>(Core.db.tasks);
//it updates only in the window from which I'm adding the row
}
viewmodel ctor
public MainWindowViewModel()
{
AddTask = new RelayCommand(OnAddTaskExecuted, p => true);
Tasks = new ObservableCollection<tasks>(Core.db.tasks);
}
So after clicking a btn I have this situation. ListView updates only in window where I click, but not in another (the new tasks is the first one)img
P.S. I have 2 same windows, I just making a new same window by btn click. That is just for a test. I'm actually creating a big project with lots of pages in it, and I need to update every Collection that have tasks in it.
Your issue is that you have two different Window instances with separate instances of ObservableCollection<tasks> Tasks
There are several ways in dotnet to structure your data in a persistent manner. Here are some:
Assuming both Windows are of the same type, the easiest way is to just make Tasks a static field.
Create a separate class, eg. called "TaskManager", with a field Tasks, and use that. If TaskManager is a static class, you don't have to bother with organizing any instances. Alternatively, there are concepts like Singletons. You can also store your data, or objects containing your data inside App.xaml.cs, where you can then access it via Application.Current.SomeField
If you want to reuse the same Window instance instead of storing your data somewhere else, that really depends on how you navigate to your Window. If you open your Window with something like this...
MyWindow window = new MyWindow();
window.Show();
... then you'll have to store your WindowInstance in a persistent manner as explained above, instead of instanciating a new one with new MyWindow() every time you want to show it.
Related
New to C#; have been a VB.NET/VBA developer for years. Am currently working on an application for our off-site workers. I have a main switchboard form with a drop-down of project numbers. The user then has the ability to open a variety of form-based tools, to include a Budgets form. On change of the drop-down, I store a Program-scoped class variable, cJob which has multiple properties (customer, location, etc). On open of the Budgets form:
Form frm = new FieldBudgets.frmBudgets().Show();
I have a private static instance of cJob called meJob (new cJob).
When going back to the switchboard, the user, without closing that instance of the Budgets for, can open additional instances of the same Budget form with different project numbers selected.
Here is my problem (sorry it took so long to get here):
A Refresh button can update the data on the Budgets form, but the new meJob information from the last instance get carried to the other instance. How can I keep these separate? I thought the new meJob would stick with the individual instances.
As an aside, I do have a less than glamorous "solution" that involves a hidden textbox and reloading the new meJob on Refresh. If that is the best solution I get, so be it...
MJ
Dictionary<string,FieldBudgets.frmBudgets> myBudgets = new Dictionary<string,FieldBudgets.frmBudgets>();
myBudgets.Add("Budget1",New FieldBudgets.frmBudgets());
myBudgets.Add("Budget2",New FieldBudgets.frmBudgets());
myBudgets.Add("Budget3",New FieldBudgets.frmBudgets());
myBudgets.Add("Budget4",New FieldBudgets.frmBudgets());
myBudgets["Budget2"].Show();
You might put any unique fields and/or properties in the constructor for frmBudgets.
I have 2 different projects that work with each other. One's a class library and another is a WPF application. So there's a function in the class library which does some data accessing (100000+ rows). My objective is to write these to the WPF app (in a view or table or something) as they are being read. However, my structure doesn't support that. This is why...
List<string> _output = new List<string>();
var output = new readBlock<DataRow>(row =>
{
_output.Add(row);
});
The above snippet is where I get each row as a string and I add them to a list. This function (in the class library) ultimately returns the list _output when its done reading all the rows. This functions prototype looks like this public static async Task<List<string>> RunAsync
So the problem here is that, when I want the data, I usually have to wait a minute or so while all the rows are being added to that list.. and only then I can see the data pouring in to my WPF view.
Ideally, every time a new item(row) is added to my list _output, I want to send it to the other project to the WPF view.
So when I call it in the WPF app like
TestList = ClassName.RunAsync(params).Result;
// Takes a minute or so to move to next line
foreach (var res in TestList)
// update view
...
Is there a better way to achieve what I'm trying to do? I would like to keep both projects separate. the WPF App and the data accessing library. Thanks for any help.
I think maybe you should read data in another thread and invoke to wpf window to update UI while reading, or pass a delegate to your class library and callback while reading lines
We have run into a strange bug that we're having problems debugging.
We have a MDI workspace that uses Microsoft CAB, DevExpress components, and .Net 3.5.
If users open two windows in the workspace that each contain a UserControl bound to two separate data models, then minimize both of them, the first window to minimize is getting it's bound fields cleared when the second one minimizes.
The .Equals and .GetHashCode methods of the data model have been overridden so that both data models are considered equal. If we change that so they are unique, we do not get this behavior.
Here's some example pseudocode showing the problem
var a = new MyWindow();
a.DataModel = new SomeClass(123);
a.ShowInMdiWorkspace();
var b = new MyWindow();
b.DataModel = new SomeClass(123);
b.ShowInMdiWorksace();
a.Minimize();
// If SomeClass.GetHashCode() is overwritten to consider two objects
// as equal based on the value passed in, then the data bindings for A
// get cleared on this call. If SomeClass.GetHashCode is unique, then
// this problem does not happen.
b.Minimize();
Here's the Call Stack when the second window gets minimized:
At the EndEditSession() call in the stack trace above, it is calling EndEditSession for the second window minimized, while by the time the Stack Trace gets past the [External Code] to the OnChange breakpoint I have set, it is firing the change method in the first window.
EndEditSession() is something custom we have implemented which looks something like this
protected void EndEditSession()
{
IBindingValue bv = null;
if (_bindingValues == null)
return;
if (_data != null)
{
foreach (KeyValuePair<string, IBindingValue> kvp in _bindingValues)
{
bv = kvp.Value;
if (bv.IsBindable)
((PropertyManager)bv.Component.BindingContext[_data]).EndCurrentEdit();
}
}
}
_bindingValues gets populated when the UserControl initializes its data bindings. The key fields are the name of the bound control, and the value fields are a custom object which stores the control itself, its name, its bound value, and default value. bv.Component returns the control that the binding is set on, which in the case of my testing is a customized DevExpress LookupEdit
_data contains the data model for the UserControl, and I can verify that it is set to the instance for the second window.
My original thought was that the BindingContext was shared so the wrong PropertyManager was being returned, however I have verified that the .BindingContext for the two forms and controls are separate.
Is it possible that having two separate copies of a UserControl bound to two separate instances of a data model would get its bindings mixed up when the GetHashCode method has been overridden so that the two objects are considered equal?
I am not very familiar with the inner workings of the WinForms binding system, or with exactly how CAB's MDI workspace gets managed.
My theory is that when the first window minimizes, it is unloading the controls to save on memory, then when the second window minimizes the internal hash table that manages the bindings is incorrectly getting confused and running an update to take data from the first minimized window (which is now blank) and updating its datasource. There are plenty of holes in this theory, however its the only thing I can think of.
I don't know the internal workings the WinForm widget, but it seems that since you've encountered an issue with overriding equals that you'd be better off working around.
If you need to evaluate equality for your own purposes:
An approach is to provide your own method to evaluate equality, rather than changing the default behavior.
If your intention is to change how the widget treats the objects:
An approach is to make a static object factory for your class. The factory could maintain a collection of all of the objects created using weak references. Weak references allow the GC to collect the objects. The factory can then check the collection of previously created objects. If a match is found then return the existing one. If not then create it. This way rather than having two different objects that evaluate two equal (override equals) you'd have a single object with two references that is equal (same memory).
Hopefully one of these other approaches will solve your problem.
BindingContext object are not sharing its fields and properties with any other BindingContext because its fields and properties are not static.
But, it is possible to have one BindingContext object for several controls.
In the first case if several controls have the same parent and have not their own BindingContext then BindingContext property of this controls will return Control.Parent(.Parent...).BindingContext object.
In the second case there are can be something like this:
var bindingContext = new BindingContext();
var a = new SomeControl();
var b = new SomeControl();
a.BindingContext = bindingContext;
b.BindingContext = bindingContext;
In the third case BindingContext can be overwritten in such a way.
I don't know what is going on in your case, so I can only recommend to do something like this before initializing data bindings:
var a = new SomeControl();
var b = new SomeControl();
a.BindingContext = new BindingContext();
b.BindingContext = new BindingContext();
If this does not solve your problem then you need to check the populating of your _bindingValues object. It is possible that during the populating of this object it is populated with wrong values.
I am trying to create a scheduler in WPF. I have a central static list in main window, which is initialized on load by a backgroundworker. There also is a dispatchtimer in mainwindow, whose interval is recalculated everytime the list changes.
public static List<ListViewcls> TODOdatalst = null;
public static void RefreshdblList()
{
if (ApplicationState.GetValue<bool>("dbDetected"))
{
TODOdatalst = DataAccess.ReadAllTODODataFromDataBase();
InitialiseDailyReminders();
}
}
Now there is another window which contains a listview which is bound to an observable collection, which is derived from the static list in mainwindow. whenever an item is added, updated or deleted from the list, the list in both the windows is manually refreshed via static functions.
private static readonly ObservableCollection<ListViewcls> TO_DOViewlst = new ObservableCollection<ListViewcls>();
public void RefreshView()
{
MainWindow.RefreshdblList();
if (MainWindow.TODOdatalst != null)
InitialiseListView(MainWindow.TODOdatalst);
else
InitialiseListView(DataAccess.ReadAllTODODataFromDataBase());
}
so is this approach proper ?? It works for now but suppose in the future, i have one more window which will also access the list but then i will be managing the refresh of data between 3 windows....that does not figure right. Anyone can suggest some better way, that i can keep a central repository and whenever it is updated all other lists get updated.
When one uses MVVM there is an option to use one ViewModel (the class which acquires and holds the data which is used as Data Contexts for a window/page(s)) between multiple windows and this scenario is common.
I would opt to share the instantiated ViewModel (or class which houses your data) between all windows and pass it in during window creation to have the window/page's data context bound to that one VM. That way the satellite window(s) are using the same data as the main and not having to do any update tricks as shown. Plus any new windows simply bind to the observable collection which everyone else is binding to.
I am creating a utility that needs to be flexible enough to use different types of data input by the user. Not just different data, as one user may enter "Rory Gallagher" another "Merle Travis" another "Louis Jordan" and yet another "Gatemouth Brown."
More like one user would enter "Rory Gallagher" another "42" and another both a date (such as 9/8/1956) and an ID value (such as "00034872184").
So the problem is with the GUI: how can I "swap out" the section of the form that prompts them for their input? It seems overkill to create several utilities that are 99% the same. But some "versions" of the app will just need a single "enter some value" label along with a TextBox, while another may need to prompt them for two or even more pieces of information.
I don't want to have an "Input Data" button on the form that would invoke another form, because when the user only needs to enter one single bit of information, that would seem quite bizarre.
I'm thinking the Strategy pattern may be in my future here, but the GUI part is still the conundrum. It would be easier if we were using WPF, but we're not. I guess I could still have a section of the form that I leave large enough to incorporate the "largest" scenario, but is that the best way to go?
UPDATE
I tried the suggestion below:
I added a panel to my form and created a user control via Add | UserControl and tried this:
panel1.Controls.Add(UserControl1);
UserControl1.Dock = DockStyle.Fill;
...but suffered an epic fail ("'PlatypiRUs.UserControl1' is a 'type' but is used like a 'variable'" and "An object reference is required for the non-static field, method, or property 'System.Windows.Forms.Control.Dock.get'")
UPDATED AGAIN
Epic success now with this:
UserControl1 usr1 = new UserControl1();
panel1.Controls.Add(usr1);
usr1.Dock = DockStyle.Fill;
I would suggest dynamically loading a different user control onto that section of the form. For design-time simplicity, you could put a panel where the dynamic control will be, and then just load it into that panel with docking turned on so it fills the whole panel. Then you can just create a separate user control for each set of data entry fields you need. For instance, if you had a panel control called pnlCustom, you could do the following in the form's load event (or wherever makes sense):
UserControl1 customControl = new UserControl1();
pnlCustom.Controls.Add(customControl);
customControl.Dock = DockStyle.Fill;
In my opinion, this is a Builder pattern.
Basically, think of your form as a "two-step view", which means the following:
Think out the structure which defines the form elements (e.g. textboxes, dropdowns, etc.),
Implement an abstract view builder (first step builder) so that in different situations you produce a different structure (remember, the structure is a common, no concrete UI components must be involved, just a structure which defines them),
Implement the abstract builder for all cases where the structure must differ,
Then implement the UI builder which takes the common structure and builds the UI coponents. Basically, this builder just translates the common structure into the platform specific UI components (such as win-forms textboxes or Html inputs).
Next step is to process the data obviously. For that you have an abstract processor and then concrete implementations for each situation (similar to the first step builder above).
Now it is clear that the UI construction and data processing can be associated 1 to 1, which means the situation is a Factory and it produces the view builder and data processor.
Pseudo code would look like this:
// construct the view.
var factory = Situation.GetCurrentSituationFactory(); //abstract factory.
var uiBuilder = factory.GetUIBuilder(); //abstract builder
var structure = uiBuilder.GetFormStructure([context goes here]); //build view definition
var viewParser = Platform.GetViewParser(); //abstract builder (step 2)
viewParser.ConstructForm([context with form goes here]); //build form UI
// later on, process the input data.
var input = viewPrser.GetInput([context with form goes here]); //input definition
var dataProcessor = factory.GetDataPocessor(); //strategy
dataProcessor.Process(input); //execute processing strategy
In addition, this does not conflict with dynamic controls or anything else you want to use to construct the form. Just implement your abstract concerns correctly.