My winform contains a TextBox that is the main control of the form. When I do CtrL + C, and often end up with an empty clipboard because for some reason the ActiveControl of the form is set to another control, like for instance the TabControl, the SplitContainer, etc. I tried to set those control TabStop = fasle, but to avail. Is there a way to prevent all controls from getting focus ? Not only regarding the mouse clicks, but also the tab keys.
I can imagine trying with some native windows messages like WM_SetFocus, or some native windows styles like WS_TABSTOP, or some control styles like ControlStyles.Selectable, or ControlStyles.UserMouse, but I'd say you cannot find a generic solution which handles all controls.
What I'm proposing here (according to your comments, looking for a better way of handling focus rather than trying to make things focusable, which makes more sense in UX point of view as well) is handling TabControl and SplitterContainer focus related events:
Tab control: Handle SelectedIndexChanged, and then move the focus to the first focusable control of the tab.
Splitter container: Handle MouseDown to trap the start of splitting, then store the active panel; later handle SplitterMoved and move the focus to he first focusable control of the active panel.
Here is what worked for me:
SplitterPanel activePanel;
private void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e)
{
splitContainer1.SelectNextControl(activePanel,
forward: true, tabStopOnly: true, nested: true, wrap: true);
}
private void splitContainer1_MouseDown(object sender, MouseEventArgs e)
{
activePanel = splitContainer1.Panel1.ContainsFocus ? splitContainer1.Panel1 :
splitContainer1.Panel2.ContainsFocus ? splitContainer1.Panel2 : null;
}
private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
{
var page = this.tabControl1.SelectedTab;
page.SelectNextControl(null,
forward: true, tabStopOnly: true, nested: true, wrap: true);
}
Instead of preventing focus, which might cause problems for those trying to navigate your app using only the keyboard, implement the IMessageFilter interface and trap Ctrl-C for your whole app. Then you can simply put the contents of your "main textbox" on the clipboard manually:
public partial class Form1 : Form, IMessageFilter
{
public Form1()
{
InitializeComponent();
Application.AddMessageFilter(this);
}
bool IMessageFilter.PreFilterMessage(ref Message m)
{
if (m.Msg == 0x0100 &&
(Keys)m.WParam.ToInt32() == Keys.C &&
ModifierKeys == Keys.Control)
{
Console.WriteLine("Ctrl-C Trapped!");
if (textBox1.SelectionLength > 0)
{
Clipboard.SetText(textBox1.SelectedText);
}
else
{
Clipboard.SetText(textBox1.Text);
}
return true;
}
return false;
}
}
Related
I'm afraid the answer is probably no...but some background. To draw a custom border on a window where the sizing logic works beyond the visible border (as it does on windows 10) I added layered windows around the edges to capture the messages and then forward them to the central window. This worked great until the form was shown modaly, at which point all the edge windows were automatically disabled. Obviously this is by design...but I'm not sure if there is some way around it. I tried making the edge windows owned by the central window, but that didn't work.
Or maybe there is a better approach entirely.
Here's a sample of the issue:
public partial class Form1 : Form
{
public Form1()
{
}
protected override void OnClick(EventArgs e)
{
base.OnClick(e);
Form f2 = new Form();
f2.Text = "Non Modal";
f2.Show();
Form f3 = new Form();
f3.Text = "Modal";
f3.ShowDialog(this);
}
}
I think you can fake the modal window, so that it is not modal but disable the caller. I used this in a own project. I did it this way:
//Setup small Interface
public interface IDialog
{
//Our own Event which tell the caller if the Dialog is active/inactive
public event DialogChangedEventArgs DialogChanged;
}
//Setup EventArgs for our own Event
public class DialogChangedEventArgs : EventArgs
{
public bool DialogActive{get;}
public DialogChangedEventArgs(bool dialogActive)
{
DialogActive = dialogActive;
}
}
//Setup the Form which act as Dialog in any other form
public class Form2 : Form, IDialog
{
public event EventHandler<DialogChangedEventArgs> DialogChanged;
//If this Form is shown we fire the Event and tell subscriber we are active
private void Form2_Shown(object sender, EventArgs e)
{
DialogChanged?.Invoke(this, true);
}
//If the user close the Form we telling subscriber we go inactive
private void Form2_Closing(object sender, CancelEventArgs e)
{
DialogChanged?.Invoke(this, false);
}
}
public class Form1 : Form
{
//Setup our Form2 and show it (not modal here!!!)
private void Initialize()
{
Form2 newForm = new Form2();
newForm.DialogChanged += DialogChanged;
newForm.Show();
}
private void Form2_DialogChanged(object sender, DialogChangedEventArgs e)
{
//Now check if Form2 is active or inactive and enable/disable Form1
//So just Form1 will be disabled.
Enable = !e.DialogActive;
}
}
It's really simple. Just use an event to tell your first Form: Hey iam second Form and active. Then you can disable the first Form with while second is active. You have the full control which forms are active or not. Hope this helps.
I need CheckBox in RibbonControl and if it checked I need to perform some task if not checked I need to perform some other task. So I tried barCheckItem1 It is working properly what I expect but it is Displaying like Button I need exact CheckBox. So again I used barEditItem1 in this Item "CheckChanged" event is not available then if i write code in "EditValueChanged" event, if I check or Uncheck the event not fired. How to complete my task ?
I need CheckBox with CheckedChanged Event.
You have two ways to access the control itself:
One way is:
CheckEdit checkEdit = barEditItem.Edit as CheckEdit;
bool isChecked = checkEdit.Checked;
The other is the repository editor directly:
bool isChecked = repositoryItemCheckedEdit.ValueChecked;
I hope this is helpful.
Is this what you need?
Add a BarEditItem with CheckEdit, attach event CheckedChanged of RepositoryItemCheckEdit. You're done.
private void repositoryItemCheckEdit1_CheckedChanged(object sender, EventArgs e)
{
Console.WriteLine(((CheckEdit) sender).Checked);
}
private void button1_Click(object sender, EventArgs e)
{
bool? ischecked = (bool?)barEditItem1.EditValue;
if(!ischecked.HasValue)
{
//In determinate state
}
else
{
if(ischecked.Value)
{
//Checked
}
else
{
//Not Checked
}
}
}
I know that default WPF behavior is to render WPF controls and then on top render WinForms, but are there any way to render WPF on top of WindowsFormsHost?
Edit: I have found a temp hack as well. When wpf control overlaps WindowsFormsHost, I change the size of the WindowsFormsHost (This only works when you have rectangular object which overlaps, doesn't work for other shapes.)
Late to the party, I know, but I recently came across this issue using a WebBrowser control.
The final fix was to create a screenshot of the web browser whenever I hosted a modal dialog over the top. Since this was a little fiddly, I turned it into a Github project, hopefully this helps a little -
https://github.com/chris84948/AirspaceFixer
(It's on Nuget too, under AirspaceFixer)
Once you have the project all you need to do is this
xmlns:asf="clr-namespace:AirspaceFixer;assembly=AirspaceFixer"
<asf:AirspacePanel FixAirspace="{Binding FixAirspace}">
<WebBrowser x:Name="Browser" />
</asf:AirspacePanel>
Where FixAirspace is the dependency property that switches from the "real" view of the content, to the screenshot or "fake" view.
This "airspace" issue is suppose to be fixed in WPF vNext. There are a couple solutions out there, such as here, here, and here.
One way to do this is to host the WPF content in a transparent Popup or Window, which overlays the Interop content.
Try this on for size:
<hacks:AirspaceOverlay>
<hacks:AirspaceOverlay.OverlayChild>
<Canvas ToolTip = "A tooltip over a DirectX surface" Background="#01000000" Name="Overlay" />
</hacks:AirspaceOverlay.OverlayChild>
<controls:OpenGLControlWrappingWindowsFormsHost />
</hacks:AirspaceOverlay>
// Adapted from http://blogs.msdn.com/b/pantal/archive/2007/07/31/managed-directx-interop-with-wpf-part-2.aspx & http://www.4mghc.com/topics/69774/1/in-wpf-how-can-you-draw-a-line-over-a-windowsformshost
public class AirspaceOverlay : Decorator
{
private readonly Window _transparentInputWindow;
private Window _parentWindow;
public AirspaceOverlay()
{
_transparentInputWindow = CreateTransparentWindow();
_transparentInputWindow.PreviewMouseDown += TransparentInputWindow_PreviewMouseDown;
}
public object OverlayChild
{
get { return _transparentInputWindow.Content; }
set { _transparentInputWindow.Content = value; }
}
private static Window CreateTransparentWindow()
{
var transparentInputWindow = new Window();
//Make the window itself transparent, with no style.
transparentInputWindow.Background = Brushes.Transparent;
transparentInputWindow.AllowsTransparency = true;
transparentInputWindow.WindowStyle = WindowStyle.None;
//Hide from taskbar until it becomes a child
transparentInputWindow.ShowInTaskbar = false;
//HACK: This window and it's child controls should never have focus, as window styling of an invisible window
//will confuse user.
transparentInputWindow.Focusable = false;
return transparentInputWindow;
}
void TransparentInputWindow_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
_parentWindow.Focus();
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
UpdateOverlaySize();
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
if (_transparentInputWindow.Visibility != Visibility.Visible)
{
UpdateOverlaySize();
_transparentInputWindow.Show();
_parentWindow = GetParentWindow(this);
_transparentInputWindow.Owner = _parentWindow;
_parentWindow.LocationChanged += ParentWindow_LocationChanged;
_parentWindow.SizeChanged += ParentWindow_SizeChanged;
}
}
private static Window GetParentWindow(DependencyObject o)
{
var parent = VisualTreeHelper.GetParent(o);
if (parent != null)
return GetParentWindow(parent);
var fe = o as FrameworkElement;
if (fe is Window)
return fe as Window;
if (fe != null && fe.Parent != null)
return GetParentWindow(fe.Parent);
throw new ApplicationException("A window parent could not be found for " + o);
}
private void ParentWindow_LocationChanged(object sender, EventArgs e)
{
UpdateOverlaySize();
}
private void ParentWindow_SizeChanged(object sender, SizeChangedEventArgs e)
{
UpdateOverlaySize();
}
private void UpdateOverlaySize()
{
var hostTopLeft = PointToScreen(new Point(0, 0));
_transparentInputWindow.Left = hostTopLeft.X;
_transparentInputWindow.Top = hostTopLeft.Y;
_transparentInputWindow.Width = ActualWidth;
_transparentInputWindow.Height = ActualHeight;
}
}
Here's a link to the best answer I've seen on this subject so far:
Can I overlay a WPF window on top of another?
If anyone finds themselves unsatisfied with the hacks, setting the Visibility of the WindowsFormsHost to Collapsed or Hidden is always an option.
I came across this issue while trying to create a MDI style interface hosting WinForms controls while porting a win forms app to WPF.
I managed to solve it by doing something like this:
WindowsFormsHost -> ElementHost -> WindowsFormsHost -> my win forms controls.
Its super ugly, but it creates windows layers for the WPF content to be under the WinForms content.
I'm trying to write an extension method that will allow me to set focus on a Control. I've written the method below which works fine, however if the control is already loaded then obviously hooking up the Loaded event isn't going to be any use - I also need a way to check if the control has been loaded so I can simply run the Focus() code without hooking up the event.
Is there any way to simulate an IsLoaded property on a control?
public static void SetFocus(this Control control)
{
// return if the control is not visible
if (control.Visibility == Visibility.Collapsed) { return; }
control.Loaded += (sender, routedeventArgs) =>
{
// focus the Silverlight plugin first
System.Windows.Browser.HtmlPage.Plugin.Focus();
control.IsTabStop = true; // required to allow Focus
control.Focus();
if (control is TextBox)
{
((TextBox)control).SelectAll();
}
};
}
EDIT: As per ColinE's answer below, I implemented it like this:
public static void SetFocus(this Control control)
{
// return if the control is not visible
if (control.Visibility == Visibility.Collapsed) { return; }
if (control.Descendants().Count() > 0)
{
// already loaded, just set focus and return
SetFocusDelegate(control);
return;
}
// not loaded, wait for load before setting focus
control.Loaded += (sender, routedeventArgs) =>
{
SetFocusDelegate(control);
};
}
public static void SetFocusDelegate(Control control)
{
// focus the Silverlight plugin first
System.Windows.Browser.HtmlPage.Plugin.Focus();
control.IsTabStop = true; // required to allow Focus
control.Focus();
if (control is TextBox)
{
((TextBox)control).SelectAll();
}
}
If a control has not been loaded, then the various elements within its template will not have been constructed. Using Linq-to-VisualTree you can confirm this:
Debug.WriteLine(control.Descendants().Count());
control.Loaded += (s, e) =>
{
Debug.WriteLine(foo.Descendants().Count());
};
The first debug output should show '0', the second will be a number >0 which indicates the number of child elements of the control once the template has been applied.
or it is enough to check the parent:
var parent = System.Windows.Media.VisualTreeHelper.GetParent(control);
if the parent is null, then the control is not loaded (because it doesn't have a parent in a visual tree)
I have a Window with seven buttons; I use it as a menu in a simple game I am working on, but I display it as a dialog. How can I know which button user has pressed, since DialogResult in WPF only offers true, false and null?
If you're making a custom Window in this way, you don't really need to worry about DialogResult.
You can keep track of this in a property within your Window, and just read the property after the dialog is closed.
MyDialog window = new MyDialog();
if (window.ShowDialog() == false)
{
// user closed the window...
}
var choice = window.CustomPropertyContainingChoice;
Define your own enum and offer a static method to display the window that return your enum.
The code below does the same thing it is part of a window that allows users to review their changes and accept or cancel. As I only need true and false I used a bool however it would be trivial to change to an enum.
public static bool DisplayChanges(List<INormalizedMessage> LstMessages)
{
var retlist = LstMessages.Where(( INormalizedMessage NM ) => { return NM.Status != NormalizedMessageStatus.NoChange; });
ReviewChanges RC = new ReviewChanges();
RC.Messages = retlist.ToList();
RC.ShowDialog();
return RC.Result;
}
private void cmdCancle_Click( object sender, RoutedEventArgs e )
{
Result = false;
Hide();
}
private void cmdOK_Click( object sender, RoutedEventArgs e )
{
Result = true;
Hide();
}