I reckon the problem of C# win forms not being repainted well in certain circumstances is covered in different places, however, I didn't manage to solve my problems by using the simple snippets I found on the web.
My problem : on a form I have a listView, which I associate to a custom data holder (2 columns, a key and a last update date). From diverse places, I need to call the updateTime(key) method which then replicated the changes in the GUI. The model gets changed but my listView never.
I have a form containing a ListView that looks like that :
partial class VolsPane : UserControl, IGUIPane
{
private ListView listView1;
private ListModel listModel1; //ListModel is 100% homemade
...
public VolsPane()
{
...
listModel1.setList(listView1);
}
}
And the class holding data for my listView is like that :
class ListModel
{
private Dictionary<string, DateTime> Underlying;
private ListView list;
...
public ListModel(string nexusKey)
{
...
}
...
public void setList(ListView list)
{
this.list = list;
}
public void updateTime(string ric)
{
Underlying[ric] = DateTime.UtcNow;
updateView();
}
public void updateView()
{
this.list.Clear();
this.list.Items.AddRange(this.underlyingToListItems());
}
...
public ListViewItem[] underlyingToListItems()
{
ListViewItem[] res = new ListViewItem[Underlying.Keys.Count];
int i = 0;
foreach (string ric in Underlying.Keys)
{
res[i] = new ListViewItem(new string[] { ric, Underlying[ric].ToString("MMM-dd hh:mm:ss") });
i++;
}
return res;
}
}
I do realize the problem is in my updateView(). In debug, the code goes there definitely. Believing the problem was to be solved with an asynchronous "invoke", I referred to this post which appeared to be a reference : Stack overflow : Automating the invoke...
Then tried this :
private void updateView()
{
if (this.list.InvokeRequired)
{
this.list.Invoke(new MethodInvoker(() => { updateView(); }));
}
else
{
this.list.Items.Clear();
//this.list.Clear();
this.list.Items.AddRange(this.underlyingToListItems());
}
}
It builds but has no effect. In debug mode, never goes in the 'if' branch, always the 'else'.
Then this :
private void updateView()
{
this.list.Invoke((MethodInvoker)delegate
{
this.list.Items.Clear();
//this.list.Clear();
this.list.Items.AddRange(this.underlyingToListItems());
});
}
I get an "InvalidOperationException : Invoke or BeginInvoke cannot be called on a control until the window handle has been created."
What is the obvious thing I must be missing here ? Or is my problem actually not the one i think ?
Thanks guys !
You are correct, the problem lies in the updateView() code. You do need to Invoke to the UI thread but the issue is the handle has not yet been created for the control. One gotcha when working with WinForms is that the InvokeRequired will actually return false if the handle has not yet been created. See this explanation form the MSDN documentation:
If the control's handle does not yet exist, InvokeRequired searches up the control's parent chain until it finds a control or form that does have a window handle. If no appropriate handle can be found, the InvokeRequired method returns false.
That's why your check for InvokeRequired was always failing. I've seen this problem fixed in a couple of ways. One solution is to attach a callback to the Control's handle created event:
public class HandleHookedListView: ListView
{
private EventHandler _handleCreatedEvent;
public HandleHookedListView(): base()
{
_handleCreatedEvent = new EventHandler(HandleHookedControl_HandleCreated);
this.HandleCreated += _handleCreatedEvent;
}
private bool _handleIsCreated;
public bool HandleIsCreated
{
get { return _handleIsCreated; }
set { _handleIsCreated = value; }
}
void HandleHookedControl_HandleCreated(object sender, EventArgs e)
{
Debug.Print("Handle Created");
this.HandleIsCreated = true;
// Unhook the delegate
if (_handleCreatedEvent != null)
this.HandleCreated -= _handleCreatedEvent;
}
}
You will then have to modify your updateView to check if the handle has been created. In this case your instance of ListView (list) has been replaced with the new HandleHookedListView
private void updateView()
{
var handleCreated = this.list.HandleIsCreated;
if (this.list.InvokeRequired && handleCreated)
{
// Handle is created and invoke is required.
this.list.Invoke(new MethodInvoker(() => { updateView(); }));
}
else if (handleCreated)
{
// In this case your control's handle has been created and invoke really
// isn't required go ahead and do the update
this.list.Items.Clear();
this.list.Items.AddRange(this.underlyingToListItems());
}
else
{
// You cannot update yet. The handle has not been created. Depending on if
// you need to "queue" these updates you can either collect them or just
// ignore them if a subsequent call to updateView() after the handle has been
// created will be sufficient
}
}
The real key here is that you are trying to do an update to the control before it has been fully initialized.
First of all
Doesn't build, invoke doesn't exist for my list model.
As far as I remember Invoke is the method of Control class.
So you can't call it in ListModel class without any instance of class inherited from Control.
Use
this.list.Invoke(
In debug mode, never goes in the 'if' branch, always the 'else'.
This may mean that this.list.InvokeRequired was called in GUI thead.
But it is also may mean that list.InvokeRequired was called before this.list was painted at least once.
This is tricky moment. If instance of Control class hasn't painted yet than gdi+ (or what is under the hood of C# WinForm painting) has not initialized yet. So nothing to synchronize.
Please double check this.
Related
I have written a user control, MenuItem, which inherits from a Form Label.
I have a backgroundworker thread whose IsBusy property is exposed through a property in the MainForm as IsBackgroundBusy.
How do I read this property from the MenuItem usercontrol? I am currently using Application.UseWaitCursor and I set that in the backgroundworker and it works perfectly, however I do not want the cursor to change. That's why I figured a property that I could set would be much better.
Here is the code in my MainForm:
public partial class MainForm : Form
{
public bool IsBackgroundBusy
{
get
{
return bwRefreshGalleries.IsBusy;
}
}
Here is the code for my usercontrol:
public partial class MenuItem: Label
{
private bool _disableIfBusy = false;
[Description("Change color if Application.UseWaitCursor is True")]
public bool DisableIfBusy
{
get
{
return _disableIfBusy;
}
set
{
_disableIfBusy = value;
}
}
public MenuItem()
{
InitializeComponent();
}
protected override void OnMouseEnter( EventArgs e )
{
if ( Application.UseWaitCursor && _disableIfBusy )
{
this.BackColor = SystemColors.ControlDark;
}
else
{
this.BackColor = SystemColors.Control;
}
base.OnMouseEnter( e );
}
(Note: it's not clear to me whether you have an actual UserControl here or not. The MenuItem class you show inherits Label, not UserControl. You should probably avoid using the term "usercontrol" or "user control" when you are not actually dealing with a UserControl object).
Absent a complete code example, it's hard to know exactly what the right solution here is. However, assuming you are using the BackgroundWorker in a typical fashion, then you simply need for the owner of the control (i.e. the containing Form) to pass the necessary state to the control as it changes. E.g.:
class MenuItem : Label
{
public bool IsParentBusy { get; set; }
}
// I.e. some method where you are handling the BackgroundWorker
void button1_Click(object sender, EventArgs e)
{
// ...some other initialization...
bwRefreshGalleries.RunWorkerCompleted += (sender1, e1) =>
{
menuItem1.IsParentBusy = false;
};
menuItem1.ParentIsBusy = true;
bwRefreshGalleries.RunAsync();
}
If you already have a handler for the RunWorkerCompleted event, then just put the statement to set the IsParentBusy property there instead of adding another handler.
Then instead of using the Application.UseWaitCursor property, you can just look at the IsParentBusy property.
There are other mechanisms you could use; I do agree with the general sentiment that the MenuItem control should not be tied to your specific Form sub-class. If for some reason the above doesn't work in your case, you need to elaborate on your question: provide a good code example and explain exactly why simply having the container of the control manage its state directly doesn't work for you
I have two forms and 1 singleton class. I am initalizing the singleton class in btn_A_Click of formA.
public partial class frmA : Form
{
public frmA()
{
InitializeComponent();
frmB frmB;
}
private void btn_A_Click(object sender, EventArgs e)
{
SessionMgmt.GetInstance().StartFormB();
}
}
This is my singleton class and here I am trying to use Forms.Invoke() method.
public class SessionMgmt
{
static SessionMgmt _sessinMgr;
frmB frB;
private SessionMgmt()
{
frB = new frmB();
}
public static SessionMgmt GetInstance()
{
if (_sessinMgr != null)
return _sessinMgr;
else
{
_sessinMgr = new SessionMgmt();
return _sessinMgr;
}
}
public bool StartFormB()
{
frB.Invoke(new EventHandler(DisplayFrmB));
return true;
}
private void DisplayFrmB(Object o, EventArgs e)
{
frB.Visible = true;
frB.Refresh();
}
}
This is my formB.
public partial class frmB : Form
{
}
But from the frB.Invoke(new EventHandler(DisplayFrmB)); method it throws the following exception:
Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
I can't figure out the issue, please help or advice me if I miss anything.
EDIT
The following structure is the way my current project is displaying the next form. It was done by VB.NET and I need to use similar kind of thing in the new project which uses C#. I saw the Invoke function which points to an event and then to a function. In that function it just makes the Form.Visible=true and Form.Refresh. But for understanding I just tried a POc and followed the same steps ,but it is not yet solved.
What is reason to call invoke? Isn't this doing the job for you?
public bool StartFormB()
{
frB.Visible = true;
return true;
}
Windows Forms is a wrapper around the Windows API, and that exception means the underlying window hasn't been created yet. I think it gets created when you set Visible to true for the first time, and there are a few other situations that do it.
See this link for a possible solution: http://blogs.msdn.com/b/mapo/archive/2011/04/27/forcing-handle-creation-in-a-net-windows-forms-control.aspx
there are two possible reasons for that exception:
The form isn't created when the invoke is called
It's possible that you're creating your controls on the wrong thread
you should always check the InvokeRequired Property before Invoking, and of course check for null before that
public bool StartFormB()
{
if (frB == null)
{
throw new ArgumentNullException("frB");
}
if (frB.InvokeRequired)
{
frB.Invoke(new EventHandler(DisplayFrmB));
}
else
{
if (frB.IsDisposed)
{
throw new ObjectDisposedException("Control is already disposed.");
}
}
return true;
}
Control handle IS NOT created if the control's Visible property is false. When you call Invoke you set your controls visible state to true in delegate, but handle is not created yet, so you can not call Invoke. So - you must call frB.CreateHandle(); after: frB = new frmB(); to force creation of control handle
private SessionMgmt()
{
frB = new frmB();
var h = frB.Handle;
}
Is it possible to design a method in such a fashion, that it knows it must automatically call a next method in succession upon exiting?
In the following example, I must call Refresh() to cause my form to repaint after this event takes place. The problem is that, it's ugly to call Refresh() after, for example, 20 different events which must make the form refresh. e.g
private void PriorityLine_Click(object sender, EventArgs e)
{
_showPriorityLine = (_showPriorityLine) ? false : true;
Refresh(); // Must call refresh for changes to take effect.
}
I suppose what I'm looking for is some kind of signature I can apply to the method to cause it to automatically chain to the next method, regardless from where its called. e.g
(I know this isn't syntactically correct.)
private void PriorityLine_Click(object sender, EventArgs e).Refresh()
{
_showPriorityLine = (_showPriorityLine) ? false : true;
}
I want to seperate the interface of the method, from the logic contained within the method. I understand it would be the exact amount of effort, if not more. For example, if I were to edit the method and accidently removed Refresh, it would cause my application to break. Whereas, if the Refresh method was outside of the actual logic of the method, I could do anything within the method without worrying about removing the next chain of logic.
Sounds like what you want is Aspect Oriented Programming, there are a number of different frameworks to enable you to have stuff "magically" happen after some set of methods have run, have a look here AOP programming in .Net?
I'm not aware of any really clean way. One method would be to use PostSharp.
You could encapsulate the changes which would cause the form to refresh into form-level properties.
For instance,
private bool _showPriorityLine;
private bool ShowPriorityLine
{
get { return _showPriorityLine; }
set
{
_showPriorityLine = value;
Refresh();
}
}
Then your event would just be
private void PriorityLine_Click(object sender, EventArgs e)
{
ShowPriorityLine = !ShowPriorityLine;
}
Of course, that only cleans up your code if you have several events manipulating the same variables that cause the form to need refreshing.
Taking into consideration your particular problem and the solutions posted, I would say the "cleanest" approach here would be to implement a Property Changed Notification just for internal use in the form i.e. you don't need to expose the event like in the MSDN example.
This way you could maintain an internal list of properties that you know will require the form to be refreshed e.g.
private List<string> _refreshProps = new List<string>();
private bool _showPriority;
public void Form()
{
_refreshProps.Add("ShowPriority");
... etc
}
// only implement properties like this that need some extra work done
public bool ShowPriority
{
get { return _showPriority; }
set
{
if (_showPriority != value)
{
_showPriority = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("ShowPriority");
}
}
}
// basic property that doesn't require anything extra
public bool AnotherProperty { get; set; }
public void Refresh()
{
// refresh the form
}
protected void OnPropertyChanged(string name)
{
if (_refreshProps.Contains(name))
Refresh();
}
The benefit of this approach is if in the future you needed to do other "stuff" after particular properties you can simply introduce another list and handle it again in your OnPropertyChanged method.
Don't call Refresh, call Invalidate. The mechanism you need is already built into Windows. Calling Invalidate simply makes a note that the window needs repainting. The operating system will eventually post a WM_PAINT message (typically after the root DispatchMessage call finishes, but the exact implementation is irrelevant).
Use a property that calls Refresh in the setter.
Something like this:
private void RefreshAfter(Action action)
{
action();
Refresh();
}
UPDATED TO MAKE IT MORE OBVIOUS:
private void DoSomeUiShiznit(Action action)
{
action();
// other parts of the code don't realize that Refresh has to be called.
// But that's cool. I got it covered.
Refresh();
}
private void PriorityLine_Click(object sender, EventArgs e)
{
DoSomeUiShiznit(() => { _showPriorityLine = !_showPriorityLine; });
}
UPDATE -- Just a message to the down-voters:
What some of you are too blind to see is that this is not all that different from:
[SomeRefreshAttribute]
private void PriorityLine_Click(object sender, EventArgs e)
{
_showPriorityLine = !_showPriorityLine;
}
Except that it is simpler, and doesn't require adding another framework to the solution. And yet the other answer suggesting as much don't get down-voted!
What's wrong with you people?
How can I make a textbox in my winforms application that accepts new lines of text from anywhere in the application?
I have a main form that contains a textbox. I'd like to directly add text to the box from a method in another class.
Update
I tried this in my main form:
public void Output(String value)
{
if (txtOutput.Text.Length > 0)
{
txtOutput.AppendText(Environment.NewLine);
}
txtOutput.AppendText(value);
}
But I can't call Output from the other class. I'm new to C#, so perhaps I'm missing something obvious.
Regards, Miel.
PS Yes, I know this is bad design, but for now this seems to be the best way to do what I want. The textbox would function like a console.
You'll need to expose the Text property of the TextBox as a string property on your form. For example...
public string TextBoxText
{
get { return textBoxName.Text; }
set { textBoxName.Text = value; }
}
Edit
After reading the question edit, your problem is that you need a reference to a specific instance of the form whereever you're trying to execute that code. You can either pass around a reference (which is the better option), or you could use some smelly code and have a static property that refers to one instance of your form. Something like...
public partial class MyForm : Form
{
private static MyForm instance;
public static MyForm Instance
{
get { return instance; }
}
public MyForm() : base()
{
InitializeComponent();
// ....
instance = this;
}
}
Using this approach, you could call MyForm.Instance.Output("test");
In order to decouple a bit more you could inverse the control a bit:
// interface for exposing append method
public interface IAppend
{
void AppendText(string text);
}
// some class that can use the IAppend interface
public class SomeOtherClass
{
private IAppend _appendTarget = null;
public SomeOtherClass(IAppend appendTarget)
{
_appendTarget = appendTarget;
}
private void AppendText(string text)
{
if (_appendTarget != null)
{
_appendTarget.AppendText(text);
}
}
public void MethodThatWillWantToAppendText()
{
// do some stuff
this.AppendText("I will add this.");
}
}
// implementation of IAppend in the form
void IAppend.AppendText(string text)
{
textBox1.AppendText(text);
}
It looks like your design is a little bit corrupted. You shouldn't let buisness logic mess with GUI controls. Why don't you try a return value and assigning it on the interface side?
This is a REALLY bad way of doing it, but just to make sure all the answers are out there...
In the VS designer, each form control has an item in the Properties window named Modifiers that defaults to Private. Changing this to one of the others settings, such as Internal or Public, will let you access it from outside the form.
I must stress that this is the worst way to do it.
I have a main window with a usercontrol. When adding code in the default constructor of the usercontrol, the designer stops showing the main window. It gives a message:
Problem loading
The document contains errors that must be fixed before the designer can be loaded.
Reload the designer after you have fixed the errors.
Reload the designer
Why is this?
This is the code that I have in the constructor:
using (var context = new Data.TVShowDataContext())
{
var list = from show in context.Shows
select show;
listShow.ItemsSource = list;
}
If I can't use the constructor to fill gui with data, when should I else do it? Would it be better to do this with binding? Any sugestions how?
The WPF designer will execute the constructor on child elements when displaying them. My guess is that you've got code in the constructor that throws an exception at design-time, probably because it's using an object that's only available at run-time. A solution would be to surround your constructor logic with a check to prevent it from executing while it's being displayed in the designer.
if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
{
using (var context = new Data.TVShowDataContext())
{
var list = from show in context.Shows
select show;
listShow.ItemsSource = list;
}
}
I would personally use an observable collection for my datasource. There are lots of examples out there, but essentially your code would look something like this. I haven't tested this code. Add some comments if you have any problems.
There are two main points here. One, don't load any data unless your not in design mode, (you could put and else statement and load stub POCO data if you need design support). Two, you should load your data on a seperate thread then your UI thread.
Updated
There was a couple of updates to the code. I changed the (new Thread) to using QueueUserWorkItem, I changed the AddItems method because ObservableCollection doesn't support AddRange, and I changed the spelling on IEnumerable
public class TvShowsDataSource
{
public ObservableCollection<Show> Shows { get; set; }
private void AddItems(IEnumerable<Show> shows)
{
foreach(var show in shows)
Shows.Add(show);
}
public void LoadShowsAsync(Dispatcher dispatcher)
{
ThreadPool.QueueUserWorkItem((state) =>
LoadShows(dispatcher));
}
private void LoadShows(Dispatcher dispatcher)
{
if (dispatcher == null)
throw new ArgumentNullException("dispatcher");
using (var context = new Data.TVShowDataContext())
{
var list = from show in context.Shows
select show;
dispatcher.Invoke(AddItems(list));
}
}
}
public class UserControl1
{
private readonly TvShowsDataSource tvShowsDataSource;
public UserControl1() : this(new TvShowsDataSource()) {}
public UserControl1(TvShowsDataSource tvShowsDataSource )
{
InitializeComponent();
this.tvShowsDataSource = tvShowsDataSource;
listShow.ItemsSource = tvShowsDataSource.Shows;
this.Loaded += UserControl1_Loaded;
}
public void UserControl1_Loaded(object sender, RoutedEventArgs e)
{
if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
{
tvShowsDataSource.LoadShowsAsync(this.Dispatcher);
}
}
}