I am using Devexpress PopupMenu to show on right click. Now I want to know before closing of this popup menu, just like Windows ContextMenu Closing event.
PopupMenu has Closeup event, but that fires after closing of it. Actually my goal is to handle when to close the popup menu according to situations.
Is there anyway, I can achieve it?
I found this previous issue - somebody tried to do the same thing using XtraBars.PopupMenu and had to create a subclass of BarManager and override the BarSelectionInfo.ClosePopup event (maybe you can adapt it to your scenario). The example project is attached to the issue and demonstrates selecting a date in the popup menu and the menu staying open.
EDIT:
Here's the relevant code for completeness - whenever the popup is about to close, ClosePopup fires, as per docs for BarManager :
When you place a BarManager on a form at design time, all controls
publish the PopupContextMenu extender property (its caption in the
Properties window looks like 'PopupContextMenu on barManager1')
You can assign the Context menu using this property and implement the override.
In the example, you return from the method based on some condition (cancel the event) - in this case the Tag of the Bar is set to False on an event in the Form and checked in the override.
private void barEditItem1_EditValueChanged(object sender, EventArgs e) {
popupMenu1.Manager.Bars[0].Tag = false;
}
using DevExpress.XtraBars;
using DevExpress.XtraBars.ViewInfo;
public class MyBarManager : BarManager {
protected override BarSelectionInfo CreateSelectionInfo() {
return new MyBarSelectionInfo(this);
}
}
public class MyBarSelectionInfo : BarSelectionInfo {
public MyBarSelectionInfo(BarManager manager)
: base(manager) {
}
public override void ClosePopup(IPopup popup) {
if (!(bool)Manager.Bars[0].Tag) {
Manager.Bars[0].Tag = true;
return;
}
base.ClosePopup(popup);
}
}
Related
I want to open a window if a button is clicked, and that button is located in another window.
So how to check whether a button in another window is clicked or not?
Now I am coding in a class called 'RightButton.cs'
I want to open a window called 'PopUp' when 'Add' button in 'Reason' window is clicked.
PaidOutReason paid = new PaidOutReason(trnprt, apiParameters);
paid.ShowDialog();
if (paid.btnSave.ClickMode == new ClickMode())
{
PopUpBanks popu = new PopUpBanks(this);
popu.Show();
}
This one was working perfectly, but I had to remove ShowDialog() and replace it with Show(). Then it was not working.
This is for a POS system. It has a user Control called 'Keyboard'. When the 'Reason' window is opening this Keyboard also want to be opened. Therefore I had to replace ShowDialog() with Show().
I'd add an event to the window, and bind an event handler to it.
class WndWindow{
BtnPaid_Click(object sender, EventArgs e){
using(var paid = new PaidOutReason()){
paid.BtnAddClick += Paid_BtnAddClick;
paid.ShowDialog();
paid.BtnAddClick -= Paid_BtnAddClick;
}
}
Paid_BtnAddClick(object sender, EventArgs e){
var popu = new PopUpBanks();
popu.Show();
}
}
class PaidOutReason{
public event EventHandler BtnAddClick;
BtnAdd_Click(object sender, EventArgs e){
//Do standard event handler code
BtnAddClick?.Invoke(this, e);
}
}
If there's any sort of checks you need to perform you can do that before reading the event, and simply return if checks fail.
You can use static controlls in your app. Start with declaring static window object in App.xaml.cs, for example
public static PaidOutReason paidOutWindow;
then, in App constructor method, after InitializingComponent(), initialize static window class:
paidOutWindow = new PaidOutReason();
You may wonder what it gives to you. Since it's POS application, you are likely to use the same set of windows quite often and repeatedly, means you can hold window object in memory and refer to it (and change, when needed). Also, after doing such thing, you will have access to all structures inside PaidOutReason object, by typing
App.paidOutWindow.FunctionName();
and finally, you should have access to all window functions such as ShowDialog().
If you are using MVVM pattern, then you can use command binding for showing the PopupBanks window.
For example:
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ICommand AddCommand { get; set; }
public ViewModel()
{
AddCommand = new RelayCommand(AddCommandHandler);
}
private void AddCommandHandler()
{
IPopUpBanks popu = new PopUpBanks(this);
popu.Show();
}
}
PaidOutReason view:
1. <Button x:Name="Add" Command={Binding AddCommand}/>
2. Set the above viewmodel as datacontext of PaidOutReason view.
Create a interface IPopUpBanks containing Show() method and implement this interface on PopUpBanks view.
Handle the Click event of btnSave:
PaidOutReason paid = new PaidOutReason(trnprt, apiParameters);
paid.btnSave.Click += (ss, ee) =>
{
PopUpBanks popu = new PopUpBanks(this);
popu.Show();
};
paid.Show();
Instead of the Keyboard Window, I made it a user control and then initialized an event in there. Then I insert that Keyboard User Control to the PaidOutReason Window and then called the event. Then I was able to use ShowDialog() to call the window.
I have a button and hidden textbox on my main form. When I press the button it will hide the main form and show the second form. When I press the button on the second form I want to show the main form again but this time I need to show the hidden textbox. I know how to pass the text from a textbox to another form but not just the reference of the textbox.
You better pass the complete main form instance for the second form, and create a public function to set the textbox to visible, or create a property around it.
Something like:
//form1
Form2 second = new Form2(this);
}....
public void ShowTextBox()
{
textbox1.Visible=true;
}
//form2
Form parent;
public Form2(Form _parent)
{
parent=_parent;
}
///later
parent.Show();
parent.ShowTextBox();
Sounds to me like a custom event would be a better approach. Have the secondary form expose an event, which is raised at whatever appropriate time (your button press). In your main form, when you create your instance of your second form, subscribe to that event. Then run your "unhide" code from within the mainform's event subscription.
This keeps the coupling down on the two forms and results in much more easily maintainable and extensible code (for best effect, use interfaces, but events are a good middle ground for learning).
Something like this:
(it's been a long time since I worked with winforms, or events even, so if this needs refining let me know)
// your secondary/popup form's class
public partial class Form2 : Form
{
// add a custom event
public EventHandler<EventArgs> MyCustomEvent;
// link up your button click event
void InitializeComponent() {
myButton.Click += myButtonClick;
}
// when your button is clicked, raise your custom event
void myButtonClick(object sender, EventArgs, e) {
onMyCustomEvent();
}
// this "broadcasts" the event
void onMyCustomEvent() {
EventHandler<EventArgs> h = MyCustomEvent;
if (h != null) {
h(this, new EventArgs());
}
}
}
// your main form's class
public partial class MainForm
{
void InitializeComponent() {
// ...
}
void showForm2() {
var form2 = new Form2();
form2.MyCustomEvent += form2CustomEvent;
form2.Show();
}
void form2CustomEvent(object sender, EventArgs e) {
myHiddenTextBox.Visible = true;
}
}
All in all this is a much better approach in terms of code architecture. Now the popup doesn't care who opens it (it has no reference to the main form), and the custom event (which is really what you're after) can be managed to any level of control you need, without interfering how other thing work (for example, perhaps later you may want to have a different action that fires this same custom event...)
Food for thought.
I have a custom control (custom ComboBox). It`s work well, when I press "arrow button" it deployed, if I press it again it deployed too, but if it are deployed and I press anywhere in my form - it close but then, when I'm trying to open it - I must press "arrow button" two times. So I need to detect this moment, when I click outside of my combobox.
Code to open ComboBox(call in ButtonClick)
private void OpenComboBox()
{
if (drop_flag)
{
...
popup.Show(this);
}
else
{
drop_flag = true;
}
}
And Close event
private void popup_Closed(object sender, ToolStripDropDownClosedEventArgs e)
{
drop_flag = false;
}
So, I want something like this
private ClickedOutsideControl()
{
dropflag = true;
}
Thats not that easy.With basic .net you always need some kind of control / references to intercept clicks by binding click events. For Example if you have an MDI Parent (Mainwindow you can intercept its clicks).
Another way (using windows api) is to hook inso system events See the following stackoverflow post Global mouse event handler.
But you should think twice if you really need this.
In the form, you can pre-process messages sent to controls by using a message filter. Here is my FAILED attempt to implement the desired functionality:
public partial class frmAutoCloseDropDown : Form, IMessageFilter
{
int _lastMsg;
public frmAutoCloseDropDown()
{
InitializeComponent();
Application.AddMessageFilter(this);
}
// THIS ATTEMPT DOES NOT WORK!
public bool PreFilterMessage(ref Message m)
{
const int WM_LBUTTONDOWN = 0x0201;
if (m.Msg!= _lastMsg) {
_lastMsg = m.Msg;
}
if (m.Msg == WM_LBUTTONDOWN) {
// You would have to do this recursively if the combo-boxes were nested inside other controls.
foreach (ComboBox cbo in Controls.OfType<ComboBox>()) {
cbo.DroppedDown = false;
}
}
return false;
}
// Note: Dispose is created inside *.Designer.cs and you have to move it manually to *.cs
protected override void Dispose(bool disposing)
{
if (disposing) {
Application.RemoveMessageFilter(this);
if (components != null) {
components.Dispose();
}
}
base.Dispose(disposing);
}
}
Why does it not work? Probably because the new messages generated by cbo.DroppedDown = false that are sent to the form to close the drop-down are appended to the message queue and are only processed after the left mouse button click has been processed. This means that even when with PreFilterMessage our attempt to close the drop-down comes too late.
A possible solution would be to re-send WM_LBUTTONDOWN to the right ComboBox. You would have to interpret the parameters of the message to get the mouse coordinates and look to which combobox they are pointing to get its HWnd. My attempt to do this also shows that the behavior also depends on the drop-down style. It also has the undesired effect to close the drown-down you just opened. And what happens if you click in the drop-down itself to select an entry?
Probably you could do it, but the code tends to become very complicated. It's not worth the effort.
Essentially I need the same thing that Form.ShowDialog() offers, but with a UserControl.
Inside a winform, I load a UserControl, which should allow a user to select an item from a list, and return it back to the caller.
For example:
var item = myUserControl.SelectItem();
Obviously, returning from a control's method is very simple. But how can I make it wait until user performs the required action with the control?
I can subscribe to an event of the control, but this path is not ideal.
Put simply, I want a UserControl's method to return after user clicks a specific button on it.
Simply put, a UserControl is really just a custom control and just like you drop a TextBox or a ListBox on your WinFrom, you drop your UserControl on the form.
Treat your UserControl just like you would treat any other control, like TextBox or ListBox.
So, just like you get the value from a TextBox through TextBox.Text or the SelectedValue or SelectedItem from a ListBox, you would call a method from your UserControl to return the SelectedItem.
Often times when the OK button is clicked or the form is closed is when in your code you would go through each of your form's controls getting their values. Presumably, you would do some validation to make sure proper values were entered, too.
Therefore, when your form is accepted is when you would call your UserControl's method to get the selected item. You don't have to subscribe to an event to wait for that to happen. Again, just treat it like you would treat a normal ListBox.
EDIT:
Knowing now more about the nature of your question this is my answer:
Say you have a UserControl that looks like this:
In the code behind you are going to have to set up an Event to monitor when the the OK button has been clicked inside the UserControl. This event will also notify a subscriber what the choice was that the user selected in your list:
public partial class SelectFromListUserControl : UserControl
{
public class SelectedItemEventArgs : EventArgs
{
public string SelectedChoice { get; set; }
}
public event EventHandler<SelectedItemEventArgs> ItemHasBeenSelected;
public SelectFromListUserControl()
{
InitializeComponent();
}
private void btnOK_Click(object sender, EventArgs e)
{
var handler = ItemHasBeenSelected;
if (handler != null)
{
handler(this, new SelectedItemEventArgs
{ SelectedChoice = listBox1.SelectedItem.ToString() });
}
}
}
On your main form you will have code patterned similar to the following.
There should be a routine to create or make visible this special user control.
It will hook the event in the user control so that the main form will be notified.
It will draw the user control.
The event handler will retrieve the value selected in the user control and then clear the user control and/or bring up another user control.
private void ShowSelectFromListWidget()
{
var uc = new SelectFromListUserControl();
uc.ItemHasBeenSelected += uc_ItemHasBeenSelected;
MakeUserControlPrimaryWindow(uc);
}
void uc_ItemHasBeenSelected(object sender,
SelectFromListUserControl.SelectedItemEventArgs e)
{
var value = e.SelectedChoice;
ClosePrimaryUserControl();
}
private void MakeUserControlPrimaryWindow(UserControl uc)
{
// my example just puts in in a panel, but for you
// put your special code here to handle your user control management
panel1.Controls.Add(uc);
}
private void ClosePrimaryUserControl()
{
// put your special code here to handle your user control management
panel1.Controls.Clear();
}
Embed it in a form and call the form modally (ShowDialog)?
But how can I make it wait until user performs the required action with the control?
The question is more about how to wait for the user to select item and click OK button without blocking entire user interface.
The answer is simple: Async/Await feature.
private readonly SelectCompletionSource as new TaskCompletionSource(of ResultType)
public async function SelectItemAsync() as ResultType
me.Visible = true
return await SelectComplectionSource.Task
end function
public function OK() as boolean
me.Visible = false
dim Result = me.SelectedItem
SelectComplectionSource.
SetResult(Result)
end function
To get an Item one calls
dim Item = await UserControl.SelectItemAsync
UserControl is shown to the user without blocking user interface. The selection task is started but paused until the result is ready.
By clicking OK button, user invokes OK function that queries selected item and makes selection task into completed state.
I want to apply fade animation every time my window is shown. How to do that from xaml? That window can be hidden and then shown again so I can't use Loaded event.
You can use the ContentRendered event or override OnContentRendered virtual method like this:
bool _shown;
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
if (_shown)
return;
_shown = true;
// Your code here.
}
You could use the
IsVisibleChanged
Event from the WPF Window;
Then in the EventMethod use:
if((bool)e.IsVisible)
{
// It became visible
}
else
{
// It became hidden
}
This works with opening a new Window instance, this.Show(), this.hide(), this.Close()