I am learning C#/Gtk# by jumping in and creating a monstrosity of an app for my own personal enjoyment/torture. My latest quandary is how to clear the items from a ComboBoxEntry. I did find a way to do it, but it seems kludgy to me.
Here is my test app, I create ComboBoxEntry with text and then click a button to clear it:
Then when I click the button to clear it, the items are removed but "foo" still remains as the active text:
Anyhow, I figured out a way to clear it with the following code. It seems that there should be a better way to do this but I can't find one so I'm coming here for a sanity check:
using System;
using Gtk;
public partial class MainWindow: Gtk.Window
{
ListStore comboModel1 = new ListStore (typeof(string));
public MainWindow (): base (Gtk.WindowType.Toplevel)
{
Build ();
ComboBoxEntry1.Model = comboModel1;
comboModel1.AppendValues ("foo");
// Set "foo" as selected item
Gtk.TreeIter iter;
ComboBoxEntry1.Model.IterNthChild (out iter, 0);
ComboBoxEntry1.SetActiveIter (iter);
}
protected void Button1OnClicked (object sender, System.EventArgs e)
{
// Just doing this .Clear () still leaves "foo" as the ActiveText
comboModel1.Clear ();
// My kludge to clear ActiveText
comboModel1.AppendValues ("");
Gtk.TreeIter iter;
ComboBoxEntry1.Model.IterNthChild (out iter, 0);
ComboBoxEntry1.SetActiveIter (iter);
comboModel1.Clear ();
}
protected void OnDeleteEvent (object sender, DeleteEventArgs a)
{
Application.Quit ();
a.RetVal = true;
}
}
Thanks!
Jason
I just tested it on MacOs 10.7 and a simple comboModel1.Clear() does the trick. There is not ActiveText and furthermore the Combobox isnt event accessible anymore cause there are no values present in the ListStore.
So maybe this is a bug on Windows. But i doubt it and will test in on Windows too.
Just as a hint instead of using IterNthChild you can use GetIterFirst.
Related
I am new to WPF and have been hunting for an answer, surely this is not difficult?
I have created a main window with links to multiple windows, and I want them to run modelessly alongside one another, but I don't want to open multiple instances of the SAME window.
In simple terms, I can have Windows A, B, C open at once, but not Windows, A, A, B, C, C.
I need to implement a check for the window I'm trying to open (in this case, EditSettings).
If open - activate it
if not open, open it.
I have the following code in Main, which is not working.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
EditSettings winEditSettings = new EditSettings();
string isOpen = null;
if (isOpen == "true")
{
winEditSettings.Activate();
}
else
{
winEditSettings.Show();
isOpen = "true";
}
}
}
Now I know what's wrong with this logic - every time I press the button to open EditSettings, it's setting isOpen to null again. If I don't set a value to isOpen, the If condition breaks.
I could initialise the variable 'isOpen' as a public variable outside the MenuItem_Click method, but then I think I would need an isOpen variable for each window I create!! Surely there is a better way?
The other option I tried is:
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
EditSettings winEditSettings = new EditSettings();
if (winEditSettings.IsLoaded)
{
winEditSettings.Activate();
}
else { winEditSettings.Show(); }
I can't figure out why this isn't working, I tried isVisible, isLoaded, isActive - nothing is stopping the window from opening more than once. Thank you for the help!
There are people who'll perhaps throw a fit at the idea, but whenever I've needed to do this, I made the child window objects part of the application. Then, in your MenuItem_Click(), test if winEditSettings is null, instead.
It's still a member variable for each window (like your provisional isOpen solution), but having the window objects available can have advantages later, if you need to bridge information between the windows. In my cases, we wanted to be able to close all the child windows together, which (most trivially) meant keeping track of those objects in a central location.
Alternatively, if you want the setup completely decoupled, you could take a singleton-like approach and put the logic into your child window classes. Specifically, you could call EditSettings.Activate and let the class keep track of whether a window needs to be created or the existing window merely Show()n.
If I were handed your code to rewrite, I'd move it something like this:
private static EditSettings winEditSettings = null;
public static void WakeUp()
{
if (winEditSettings == null)
{
winEditSettings = new EditSettings();
}
winEditSettings.Activate(); // This may need to be inside the block above
winEditSettings.Show();
}
Both of those are part of the class (static), rather than an instance. Your application object therefore calls EditSettings.WakeUp() inside the original MenuItem_Click(), and never actually sees the child window, itself.
If you change your mind about the decoupled architecture later, by the way, you can add a get accessor to your winEditSettings and keep everybody fairly happy.
if (_adCst == null)
{
_adCst = new AddCustomerPage();
_adCst.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
_adCst.WindowState = System.Windows.WindowState.Normal;
_adCst.ResizeMode = System.Windows.ResizeMode.NoResize;
_adCst.Activate(); // This may need to be inside the block above
_adCst.Show();
}
else
{
if (!_adCst.IsLoaded == true)
{
_adCst = new AddCustomerPage();
_adCst.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
_adCst.WindowState = System.Windows.WindowState.Normal;
_adCst.ResizeMode = System.Windows.ResizeMode.NoResize;
_adCst.Show();
}
_adCst.Activate();
}
My suggestion would be that you set some form of a counter. This will prevent more than one instance of the window being opened.
int windowOpen = 1;
private void button_Click(object sender, RoutedEventArgs e)
{
if (windowOpen == 1)
{
WindowA winA = new WindowA();
winA.Show();
windowOpen++; //increments windowOpen by 1, windowOpen will now = 2
}
else if (windowOpen > 1)
{
MessageBox.Show("Window is already open");
}
}
I hope this helps.
For anyone else with this question, I have found another solution - which works except that it doesn't manage to bring the open window to the front (Activate). It does, however, prevent opening the same window more than once.
foreach (Window n in Application.Current.Windows)
if (n.Name == "winEditSettings")
{ winEditSettings.Activate(); }
else
{ winEditSettings.Show(); }
Can anyone speculate on why the window is not brought to the front, with Activate()?
EDIT
For others with this question, placing the winEditSettings.Activate() outside of the foreach loop does everything I'm trying to achieve:
foreach (Window n in Application.Current.Windows)
if (n.Name == "winEditSettings")
{ }
else
{ winEditSettings.Show(); }
winEditSettings.Activate();
This will stop multiple instances of the same window from opening, and will bring the window to the front if the user attempts to reopen it.
I have a class Viewer that creates two linked FastColoredTextBoxes. I want the two boxes to scroll together horizontally. I have this code:
public class Viewer : Panel
{
public FastColoredTextBox HeaderRow = new FastColoredTextBox();
public FastColoredTextBox Editor = new FastColoredTextBox();
public Viewer(int _Top, int _Left, int _Height, int _Width, bool _HasHeaderRow, Control control)
{
this.Editor.Scroll += new ScrollEventHandler(Editor_Scroll);
}
void Editor_Scroll(object sender, ScrollEventArgs e)
{
if (e.ScrollOrientation == ScrollOrientation.HorizontalScroll)
{
this.HeaderRow.HorizontalScroll.Value = this.Editor.HorizontalScroll.Value;
}
this.HeaderRow.UpdateScrollbars();
}
}
It doesn't work. I've never tried to do attach events to controls in a class instance before. If I declare the controls in my form and attach a very similar event (minus the .this's) it works fine. Thank you.
i think that for the next time try to tell yourself " what could it be?" and maybe debug a little, like a breackpoint for example. as you probably understood, you had a little mistake in the line
this.HeaderRow.HorizontalScroll.Value = this.HeaderRow.HorizontalScroll.Value;
you meant to write
HeaderRow.HorizontalScroll.Value = Editor.HorizontalScroll.Value;
you just got mixed between the two or something, which happens to all of us. but the first thing i would do is to think and debug it, check the values and let someone look at it. only then post it here.
I have an API (dll) that collects stock ticks via an event mechanism. Such as below:
...
using MT4API;
public partial class Blue : Form
{
...
public Blue()
{
...
string symbol = "GBPUSD";
MT4DDE dde = new MT4DDE("");
dde.OnQuote += new System.EventHandler<QuoteEventArgs>(MT_OnQuote);
dde.Connect();
dde.Subscribe(symbol);
....
The idea is that on each chart tick I get an event. here is the event handler code:
private static void MT_OnQuote(object sender, QuoteEventArgs args)
{
GlobalClass.Ask = args.Ask;
GlobalClass.Bid = args.Bid;
// I have back ground worker code that updatestables from the global class
}
This all works fine. So long as I do not touch any other buttons on the form UI. As soon as I click a button on the form of the UI... I no longer receive events from my API, the UI application functions normally, but with no data from the API.
Why do events from the UI stop any further events coming from the API event?
Any idea whats going on here? Or suggestions how to design this?
Does the same problem occur if you comment out your code that updates the tables from the global object? and if you comment out the background worker?
It would be a good idea to distinguish if the event stops being fired just after you press some button on the UI, or if it stops being fired only after some line of code you wrote is being executed.
In order to be able to help you, we would need to know how the event on the MT4DDE class is triggered.
If you have the code for this class, posting it would help.
If you don't you may want to use a tool such as Reflector to decompile the assembly into C# and see what the MT4DDE class is doing that might cause it to stop invoking the event.
In addition, if you are doing anything related to background threads, or if you're doing anything unusual with your application's main message loop, it would be a good idea to mention it here.
I have tried to use the invoke command, it works, but after a few events it stops...here is the code isolated:
using MT4API;
namespace WindowsFormsApplication1
{
public delegate void UpdateTextCallback(double ask, double bid);
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
string symbol = "GBPUSD";
MT4DDE dde = new MT4DDE("");
dde.OnQuote += new EventHandler<QuoteEventArgs>(MT_OnQuote);
dde.Connect();
dde.Subscribe(symbol);
}
private void updateTickDisplay(double ask, double bid)
{
textBox1.Text = ask.ToString();
textBox2.Text = bid.ToString();
}
private void button1_Click(object sender, EventArgs e)
{
this.Close();
}
private void MT_OnQuote(object sender, QuoteEventArgs args)
{
BeginInvoke(new UpdateTextCallback(this.updateTickDisplay),
new object[] { args.Ask, args.Bid });
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
textBox3.Text = textBox1.Text;
}
}
}
The only difference from the real code is that I am using a data grid....as opposed to a text field. But it is clear that the UI blocks somehow the new events. It is strange that I get about 5 to 10 events and then it just stops. Strange. Any ideas on a differnet design?
This is a .net problem with winforms, not asp.net.
I have a windows form with several tabs. I set data bindings of all controls when the form is loaded. But I have noticed that the data bindings of controls on the second tab do not work. Those bindings work only when the form is loaded and when I select the second tab. This brings the suspicion to me: data bindings work only when bound controls become visible.
Anyone can tell me whether this is true or not? It is not hard to test this but I would like to know some confirmation.
Thanks
You are correct. A data-bound control are not updated until the control is made visible.
The only reference I can find for this at the moment is this MSDN thread.
Your issue has to do with the behavior of the TabControl. See Microsoft bug report. I posted a workaround for that problem which subclasses the TabControl and 'Iniatalizes' all the tab pages when the control is created or the handle is created. Below is the code for the workaround.
public partial class TabControl : System.Windows.Forms.TabControl
{
protected override void OnHandleCreated(EventArgs e_)
{
base.OnHandleCreated(e_);
foreach (System.Windows.Forms.TabPage tabPage in TabPages)
{
InitializeTabPage(tabPage, true, Created);
}
}
protected override void OnControlAdded(ControlEventArgs e_)
{
base.OnControlAdded(e_);
System.Windows.Forms.TabPage page = e_.Control as System.Windows.Forms.TabPage;
if ((page != null) && (page.Parent == this) && (IsHandleCreated || Created))
{
InitializeTabPage(page, IsHandleCreated, Created);
}
}
protected override void OnCreateControl()
{
base.OnCreateControl();
foreach (System.Windows.Forms.TabPage tabPage in TabPages)
{
InitializeTabPage(tabPage, IsHandleCreated, true);
}
}
//PRB: Exception thrown during Windows Forms data binding if bound control is on a tab page with uncreated handle
//FIX: Make sure all tab pages are created when the tabcontrol is created.
//https://connect.microsoft.com/VisualStudio/feedback/details/351177
private void InitializeTabPage(System.Windows.Forms.TabPage page_, bool createHandle_, bool createControl_)
{
if (!createControl_ && !createHandle_)
{
return;
}
if (createHandle_ && !page_.IsHandleCreated)
{
IntPtr handle = page_.Handle;
}
if (!page_.Created && createControl_)
{
return;
}
bool visible = page_.Visible;
if (!visible)
{
page_.Visible = true;
}
page_.CreateControl();
if (!visible)
{
page_.Visible = false;
}
}
}
We've encountered a similar problem. We're trying to write to 2 bound, invisible fields so that we can change the format that we write to our dataset. This works fine when the objects are visible, but stops working when the visible property was changed to false.
To get round it, I added the following code:
// Stop our screen flickering.
chSplitContainer.Panel2.SuspendLayout();
// Make the bound fields visible or the binding doesn't work.
tbxValueCr.Visible = true;
tbxValueDb.Visible = true;
// Update the fields here.
<DO STUFF>
// Restore settings to how they were, so you don't know we're here.
tbxValueCr.Visible = false;
tbxValueDb.Visible = false;
chSplitContainer.Panel2.ResumeLayout();
I've struggled with this myself and concluded that the only workaround, besides subclassing apparently (see hjb417's answer), was to make the other tab visible. Switching to the other tab and going back to the previous immediately before the form is visible doesn't work. If you do not want to have the second tab visible, I've used the following code as a workaround:
this.tabControl.SelectedTab = this.tabPageB;
this.tabPageB.BindingContextChanged += (object sender, EventArgs e) => {
this.tabContainerMain.SelectedTab = this.tabPageA;
};
Assuming tabPageA is the visible tab, and tabPageB is the invisible one you want to initialize. This switches to pageB, and switches back once the data binding is complete. This is invisible to the user in the Form.
Still an ugly hack, but at least this works. Off course, he code gets even uglier when you have multiple tabs.
Sorry for necromancing this thread, but it is easy to force the invisible controls' databinding/handles to be ready using this method:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/190296c5-c3b1-4d67-a4a7-ad3cdc55da06/problem-with-binding-and-tabcontrol?forum=winforms
Simply, let's say if your controls are in tab page tpg_Second (or tabCtl.TabPages[1]), before you do anything with their data, call this first:
tpg_Second.Show()
This will not activate any of the tab pages, but viola, the databinding of the controls should work now.
This is not something I've come across directly. However, you might be experiencing a problem with the BindingContext. Without more details it's hard to say, but if I were you I'd set a breakpoint and make sure the controls are all bound in the same context.
Based on the answers, I made this method that works for me:
public partial class Form1: Form
{
private void Form1_Load(object sender, EventArgs e)
{
...
forceBindTabs(tabControl1);
}
private void forceBindTabs(TabControl ctl)
{
ctl.SuspendLayout();
foreach (TabPage tab in ctl.TabPages)
tab.Visible = true;
ctl.ResumeLayout();
}
}
In addition to solving the problem, the tabs are loaded at the beginning and are displayed faster when the user clicks on them.
I have a list view that is periodically updated (every 60 seconds). It was anoying to me that i would get a flicker every time it up dated. The method being used was to clear all the items and then recreate them. I decided to instead of clearing the items I would just write directly to the cell with the new text. Is this a better approach or does anyone have a better solution.
The ListView control has a flicker issue. The problem appears to be that the control's Update overload is improperly implemented such that it acts like a Refresh. An Update should cause the control to redraw only its invalid regions whereas a Refresh redraws the control’s entire client area. So if you were to change, say, the background color of one item in the list then only that particular item should need to be repainted. Unfortunately, the ListView control seems to be of a different opinion and wants to repaint its entire surface whenever you mess with a single item… even if the item is not currently being displayed. So, anyways, you can easily suppress the flicker by rolling your own as follows:
class ListViewNF : System.Windows.Forms.ListView
{
public ListViewNF()
{
//Activate double buffering
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
//Enable the OnNotifyMessage event so we get a chance to filter out
// Windows messages before they get to the form's WndProc
this.SetStyle(ControlStyles.EnableNotifyMessage, true);
}
protected override void OnNotifyMessage(Message m)
{
//Filter out the WM_ERASEBKGND message
if(m.Msg != 0x14)
{
base.OnNotifyMessage(m);
}
}
}
From: Geekswithblogs.net
In addition to the other replies, many controls have a [Begin|End]Update() method that you can use to reduce flickering when editing the contents - for example:
listView.BeginUpdate();
try {
// listView.Items... (lots of editing)
} finally {
listView.EndUpdate();
}
Here is my quick fix for a C# implementation that does not require subclassing the list views etc.
Uses reflection to set the DoubleBuffered Property to true in the forms constructor.
lvMessages
.GetType()
.GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
.SetValue(lvMessages, true, null);
Update for 2021:
I got pinged on this old post with a comment and I would write this code differently now. Below is an extension method that will add a new method to a ListView to be able to set the double buffered property to true/false as required. This will then extend all list views and make it easier to call as reqired.
/// <summary>
/// Extension methods for List Views
/// </summary>
public static class ListViewExtensions
{
/// <summary>
/// Sets the double buffered property of a list view to the specified value
/// </summary>
/// <param name="listView">The List view</param>
/// <param name="doubleBuffered">Double Buffered or not</param>
public static void SetDoubleBuffered(this System.Windows.Forms.ListView listView, bool doubleBuffered = true)
{
listView
.GetType()
.GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
.SetValue(listView, doubleBuffered, null);
}
}
If this can help, the following component solved my ListView flickering issues with .NET 3.5
[ToolboxItem(true)]
[ToolboxBitmap(typeof(ListView))]
public class ListViewDoubleBuffered : ListView
{
public ListViewDoubleBuffered()
{
this.DoubleBuffered = true;
}
}
I use it in conjonction with .BeginUpdate() and .EndUpdate() methods where I do ListView.Items manipulation.
I don't understand why this property is a protected one...even in the .NET 4.5 (maybe a security issue)
Yes, make it double buffered. It will reduce the flicker ;) http://msdn.microsoft.com/en-us/library/system.windows.forms.listview.doublebuffered.aspx
Excellent question and Stormenent's answer was spot on. Here's a C++ port of his code for anyone else who might be tackling C++/CLI implementations.
#pragma once
#include "Windows.h" // For WM_ERASEBKGND
using namespace System;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
public ref class FlickerFreeListView : public ListView
{
public:
FlickerFreeListView()
{
//Activate double buffering
SetStyle(ControlStyles::OptimizedDoubleBuffer | ControlStyles::AllPaintingInWmPaint, true);
//Enable the OnNotifyMessage event so we get a chance to filter out
// Windows messages before they get to the form's WndProc
SetStyle(ControlStyles::EnableNotifyMessage, true);
}
protected:
virtual void OnNotifyMessage(Message m) override
{
//Filter out the WM_ERASEBKGND message
if(m.Msg != WM_ERASEBKGND)
{
ListView::OnNotifyMessage(m);
}
}
};
You can use the following extension class to set the DoubleBuffered property to true:
using System.Reflection;
public static class ListViewExtensions
{
public static void SetDoubleBuffered(this ListView listView, bool value)
{
listView.GetType()
.GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(listView, value);
}
}
The simplest Solution would probably be using
listView.Items.AddRange(listViewItems.ToArray());
instead of
foreach (ListViewItem listViewItem in listViewItems)
{
listView.Items.Add(listViewItem);
}
This works way better.
Simple solution
yourlistview.BeginUpdate()
//Do your update of adding and removing item from the list
yourlistview.EndUpdate()
I know this is an extremely old question and answer. However, this is the top result when searching for "C++/cli listview flicker" - despite the fact that this isn't even talking about C++. So here's the C++ version of this:
I put this in the header file for my main form, you can choose to put it elsewhere...
static void DoubleBuffer(Control^ control, bool enable) {
System::Reflection::PropertyInfo^ info = control->GetType()->
GetProperty("DoubleBuffered", System::Reflection::BindingFlags::Instance
| System::Reflection::BindingFlags::NonPublic);
info->SetValue(control, enable, nullptr);
}
If you happen to land here looking for a similar answer for managed C++, that works for me. :)
This worked best for me.
Since you are editing the cell directly, the best solution in your case would be to simply refresh/reload that particular cell/row instead of the entire table.
You could use the RedrawItems(...) method that basically repaints only the specified range of items/rows of the listview.
public void RedrawItems(int startIndex, int endIndex, bool invalidateOnly);
Reference
This totally got rid of the full listview flicker for me.
Only the relevant item/record flickers while getting updated.
Cheers!
Try setting the double buffered property in true.
Also you could use:
this.SuspendLayout();
//update control
this.ResumeLayout(False);
this.PerformLayout();
In Winrt Windows phone 8.1 you can set the following code to fix this issue.
<ListView.ItemContainerTransitions>
<TransitionCollection/>
</ListView.ItemContainerTransitions>
For what it's worth, in my case, I simply had to add a call to
Application.EnableVisualStyles()
before running the application, like this:
private static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Form1());
}
Otherwise, double buffering is not enough. Maybe it was a very old project and new ones have that setting by default...
If someone would still look an answer for this, I used a timer for a slight delay and it solved the problem nicely. I wanted to highlight (change colour) for the entire row on mouse move event, but I think it would work for item replacement etc. For me listView.BeginUpdate() and listView.EndUpdate() didn't work, DoubleBuffered property also didn't work, I have googled a lot and nothing worked.
private int currentViewItemIndex;
private int lastViewItemIndex;
private void listView_MouseMove(object sender, MouseEventArgs e)
{
ListViewItem lvi = listView.GetItemAt(e.X, e.Y);
if (lvi != null && lastViewItemIndex == -1)
{
listView.Items[lvi.Index].BackColor = Color.Green;
lastViewItemIndex = lvi.Index;
}
if (lvi != null && lastViewItemIndex != -1)
{
currentViewItemIndex = lvi.Index;
listViewTimer.Start();
}
}
private void listViewTimer_Tick(object sender, EventArgs e)
{
listView.BeginUpdate();
listView.Items[lastViewItemIndex].BackColor = Colour.Transparent;
listView.Items[currentViewItemIndex].BackColor = Colour.Green;
listView.EndUpdate();
lastViewItemIndex = currentViewItemIndex;
listViewTimer.Stop();
}