I have listview control. I need to add semi-transparent button with image to listview. Something like this:
I found several projects that use semi-transparent buttons on the form. But when I transfer them to the ListView, they do not work.
Necessary to use .net 2.0 framework.
I found some solution.
Making Transparent Controls - No Flickering
I inherit my TransparentToggleButton class from TranspControl class:
public class TransparentToggleButton : TranspControl
{
private Image _normalState;
private Image _mouseUpState;
private Image _activateState;
private bool _state;
private bool _mouseUnder;
public event EventHandler StateChanged;
public bool ToggleState
{
get { return _state; }
set
{
_state = value;
SetImage();
}
}
public void SetImages(Image normalState, Image mouseUpState, Image activateState)
{
BackImage = normalState;
_normalState = normalState;
_mouseUpState = mouseUpState;
_activateState = activateState;
}
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
if (e.Button == MouseButtons.Left)
{
_state = !_state;
if (StateChanged != null)
StateChanged(this, e);
SetImage();
}
}
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
_mouseUnder = true;
SetImage();
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
_mouseUnder = false;
SetImage();
}
private void SetImage()
{
try
{
if (_state)
BackImage = _activateState;
else
BackImage = _mouseUnder ? _mouseUpState : _normalState;
}
catch (Exception)
{
}
}
}
Function SetImages loads the 3 images that used for normal state, normal state when cursor over the button, activate state.
Besides need catch listview scroll event and Invalidate() TransparentToggleButton.
Related
I have a custom component for WinForms, on which graphics are drawn.
Using the Ctrl+right/left mouse buttons, I can add or remove objects.
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (e.KeyCode == Keys.ControlKey)
this.EditorMode = true;
}
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
if (e.KeyCode == Keys.ControlKey)
this.EditorMode = false;
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (!this.EditorMode)
{
base.OnMouseDown(e);
return;
}
if (e.Button == MouseButtons.Left)
{
// adding new object
}
else if (e.Button == MouseButtons.Right)
{
// deleting object
}
}
Everything works fine until I add something else to the custom control.
The problem is that pressing the Ctrl key will no longer be handled by the controller, but by the element on which the focus is currently set.
And I need my keyboard shortcut to work regardless of which element the focus is on...
What is the best way to do this?
I tried to redefine Processcmdkey, but it does not allow me to know if the key was pressed or released
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == (Keys.ControlKey | Keys.Control))
{
MessageBox.Show("ctrl");
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
what should I do to get the desired result: regardless of focusing on child controls, I can always add new objects to the drawing?
My suggestion is to implement IMessageFilter in your MainForm and intercept
the WM_KEYDOWN message for Control-Left and Control-Right. The example below is a guideline for a UserControl that will add and remove buttons based on Control-Right and Control-Left respectively.
Adding and Removing the MessageFilter
The message filter will be added on the OnHandleCreated override and removed in the Dispose method of MainForm.
public partial class MainForm : Form, IMessageFilter
{
public MainForm() => InitializeComponent();
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if(!(DesignMode ) || _isHandleInitialized)
{
_isHandleInitialized = true; ;
Application.AddMessageFilter(this);
}
}
bool _isHandleInitialized = false;
// In MainForm.Designer.cs
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
Application.RemoveMessageFilter(this);
}
base.Dispose(disposing);
}
}
Handling PreFilterMessage
The MainForm will provide three event hooks for ControlLeft, ControlRight, and NoCommand. These will be fired in the PreFilterMessage method.
public partial class MainForm : Form, IMessageFilter
{
const int WM_KEYDOWN = 0x100;
public bool PreFilterMessage(ref Message m)
{
if (Form.ActiveForm == this)
{
switch (m.Msg)
{
case WM_KEYDOWN:
var key = (Keys)m.WParam | ModifierKeys;
switch (key)
{
case Keys.Control | Keys.Left:
ControlLeft?.Invoke(this, EventArgs.Empty);
Text = "Control.Left";
break;
case Keys.Control | Keys.Right:
ControlRight?.Invoke(this, EventArgs.Empty);
Text = "Control.Right";
break;
default:
// Don't event if it's "just" the Control key
if(ModifierKeys == Keys.None)
{
Text = "Main Form";
NoCommand?.Invoke(this, EventArgs.Empty);
}
break;
}
break;
}
}
return false;
}
public event EventHandler ControlLeft;
public event EventHandler ControlRight;
public event EventHandler NoCommand;
}
Responding to events in the UserControl
The MainForm events will be subscribed to in the OnHandleCreated override of UserControlResponder.
public partial class UserControlResponder : UserControl
{
public UserControlResponder()
{
InitializeComponent();
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if(!(DesignMode || _isHandleInitialized))
{
_isHandleInitialized = true;
var main = (MainForm)Parent;
main.ControlLeft += onControlLeft;
main.ControlRight += onControlRight;
main.NoCommand += onNoCommand;
}
}
private bool _isHandleInitialized = false;
char _tstCount = 'A';
private void onControlRight(object sender, EventArgs e)
{
BackColor = Color.LightBlue;
BorderStyle = BorderStyle.None;
var button = new Button
{
Text = $"Button {_tstCount++}",
Size = new Size(150, 50),
};
button.Click += onAnyButtonClick;
flowLayoutPanel.Controls.Add(button);
}
private void onControlLeft(object sender, EventArgs e)
{
BackColor = Color.LightGreen;
BorderStyle = BorderStyle.None;
if(flowLayoutPanel.Controls.Count != 0)
{
var remove = flowLayoutPanel.Controls[flowLayoutPanel.Controls.Count - 1];
if(remove is Button button)
{
button.Click -= onAnyButtonClick;
}
flowLayoutPanel.Controls.RemoveAt(flowLayoutPanel.Controls.Count - 1);
}
}
private void onNoCommand(object sender, EventArgs e)
{
BackColor = Color.Transparent;
BorderStyle = BorderStyle.FixedSingle;
}
private void onAnyButtonClick(object sender, EventArgs e)
{
((MainForm)Parent).Text = $"{((Button)sender).Text} Clicked";
}
}
I wanted to hide the search bar icon in IOS using custom SearchBarRenderer based on the IsFocused property. How can I achieve this?
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (Element == null || Control == null)
return;
var element = Element as CustomSearchBar;
if (element.IsFocused)
{
}
else
{
}
}
If you are using Custom Renderer to realize it , code as follow .
[assembly: ExportRenderer(typeof(CustomSearchBar), typeof(CustomSearchBarRenderer))]
namespace App15.iOS
{
public class CustomSearchBarRenderer : SearchBarRenderer
{
UIImage searchImg;
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
{
base.OnElementChanged(e);
searchImg = Control.GetImageForSearchBarIcon(UISearchBarIcon.Search, UIControlState.Normal);
if (e.OldElement != null)
{
Control.OnEditingStarted -= Control_OnEditingStarted;
Control.OnEditingStopped -= Control_OnEditingStopped;
}
if (e.NewElement != null)
{
if (null != Control)
{
Control.OnEditingStarted += Control_OnEditingStarted;
Control.OnEditingStopped += Control_OnEditingStopped;
}
}
}
private void Control_OnEditingStopped(object sender, EventArgs e)
{
var searchBar = sender as UISearchBar;
searchBar.SetImageforSearchBarIcon(searchImg, UISearchBarIcon.Search, UIControlState.Normal);
}
private void Control_OnEditingStarted(object sender, EventArgs e)
{
var searchBar = sender as UISearchBar;
searchBar.SetImageforSearchBarIcon(new UIImage(), UISearchBarIcon.Search, UIControlState.Normal);
}
}
}
Note: App15 is namespace in my local project.
As document Introduction to Effects of effects said , you also can refer to this document Creating an Effect to use Effect to realize it ,because it no need to use Custom Renderer to do that .
Sample of Effects as follow:
Creating SearchBarEffects :
public class SearchBarEffects : RoutingEffect
{
public SearchBarEffects() : base($"MyCompany.{nameof(SearchBarEffects)}")
{
}
}
Using in Xaml :
// here is custom renderer
<local:CustomSearchBar Placeholder="renderer input"/>
// here is custom effect
<SearchBar Placeholder="effect input">
<SearchBar.Effects>
<local:SearchBarEffects />
</SearchBar.Effects>
</SearchBar>
Creating SearchBarEffect in iOS solution :
[assembly: ResolutionGroupName("MyCompany")]
[assembly: ExportEffect(typeof(App15.iOS.SearchBarEffect), "SearchBarEffects")]
namespace App15.iOS
{
public class SearchBarEffect : PlatformEffect
{
UIImage searchImg;
protected override void OnAttached()
{
searchImg = ((UISearchBar)Control).GetImageForSearchBarIcon(UISearchBarIcon.Search, UIControlState.Normal);
((UISearchBar)Control).OnEditingStarted += SearchBar_OnEditingStarted;
((UISearchBar)Control).OnEditingStopped += SearchBar_OnEditingStopped;
}
private void SearchBar_OnEditingStopped(object sender, EventArgs e)
{
var searchBar = sender as UISearchBar;
searchBar.SetImageforSearchBarIcon(searchImg, UISearchBarIcon.Search, UIControlState.Normal);
}
private void SearchBar_OnEditingStarted(object sender, EventArgs e)
{
var searchBar = sender as UISearchBar;
searchBar.SetImageforSearchBarIcon(new UIImage(), UISearchBarIcon.Search, UIControlState.Normal);
}
protected override void OnDetached()
{
((UISearchBar)Control).OnEditingStarted -= SearchBar_OnEditingStarted;
((UISearchBar)Control).OnEditingStopped -= SearchBar_OnEditingStopped;
}
}
}
Finally ,showing the effect by two ways . Up is Effect's , down is Renderer's .
Personal note - there is no need to use a Custom Renderer for this, an effect is more than enough - I tested with Effect, not with custom renderer. On how to create Effects, see here
this.Control will be a UISearchBar if you only add this effect to <SearchBar> elements.
So you can do this (based on iOS and Xamarin.iOS documentation):
iOS implementation of effect:
public class NoSearchIconEffect : PlatformEffect
{
UIImage defaultIcon = null;
protected override void OnAttached()
{
var searchBar = this.Control as UISearchBar;
defaultIcon = defaultIcon ?? searchBar.GetImageForSearchBarIcon(UISearchBarIcon.Search, UIControlState.Normal); //This will save the default icon
searchBar.OnEditingStarted += delegate
{
searchBar.SetImageforSearchBarIcon(new UIImage(), UISearchBarIcon.Search, UIControlState.Normal);
};
searchBar.OnEditingStopped += delegate
{
searchBar.SetImageforSearchBarIcon(defaultIcon, UISearchBarIcon.Search, UIControlState.Normal);
};
}
protected override void OnDetached()
{
}
}
I want to hide a button when the user is scrolling through the page so a specific area of the page is not blocked by the button, after the user finish scrolling I will show the button again after a delay of 1000-1500ms. My idea so far is that I will subscribe to the scroll changing event, hide the button, wait for specific delay, show the button again. I managed to implement it for Android platform using effect like this:
public class AndroidScrollingEffect : Xamarin.Forms.Platform.Android.PlatformEffect
{
private bool _isAttached;
public static void Initialize() { }
protected override void OnAttached()
{
if (!_isAttached)
{
Control.ScrollChange += Control_ScrollChange;
_isAttached = true;
}
}
private void Control_ScrollChange(object sender, global::Android.Views.View.ScrollChangeEventArgs e)
{
var command = ScrollingEffect.GetCommand(Element);
command?.Execute(null);
}
protected override void OnDetached()
{
if (_isAttached)
{
Control.ScrollChange -= Control_ScrollChange;
_isAttached = false;
}
}
}
The implementation is working just fine, for android platform. Now, I want to do the same thing for iOS. So, is there an equivalent event for ScrollChange on iOS?
P.S. I want to use effect so I don't write code in the code behind of the page and still able to bind to a specific command in the view model.
Forms ScrollView is rendered as UIScrollView on iOS. So you can try the code below to construct your custom effect:
public class iOSScrollingEffect : PlatformEffect
{
UIScrollView nativeControl;
private bool _isAttached;
protected override void OnAttached()
{
nativeControl = Control as UIScrollView;
if (nativeControl != null && !_isAttached)
{
nativeControl.Scrolled += NativeControl_Scrolled;
_isAttached = true;
}
}
private void NativeControl_Scrolled(object sender, EventArgs e)
{
...
}
protected override void OnDetached()
{
if (_isAttached)
{
nativeControl.Scrolled -= NativeControl_Scrolled;
_isAttached = false;
}
}
}
I'm extending the control canvas and adding my own custom overrides for MouseEvents. I was curious to know why this basic override which is when the user presses any key on the keyboard it doesn't emit a signal. How can I make this override work in wpf c#?
namespace CanvasGraphDemo
{
public class CanvasGraph : Canvas
{
public CanvasGraph()
{
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (e.Key == Key.Enter)
{
Console.WriteLine("context menu open");
e.Handled = true;
}
}
}
}
This will work with your specific example. As others noted, you have to make the Canvas focusable and actually focus it, so it will receive keyboard events.
public class CanvasGraph : Canvas
{
public CanvasGraph()
{
Focusable = true;
Loaded += OnCanvasGraphLoaded;
}
private void OnCanvasGraphLoaded(object sender, RoutedEventArgs routedEventArgs)
{
Focus();
Loaded -= OnCanvasGraphLoaded;
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (e.Key == Key.Enter)
{
Console.WriteLine("context menu open");
e.Handled = true;
}
}
}
I need to display a Form as a ToolTip of an UserControl. When the mouse is over the UserControl, the Form have to be show, and when the mouse leave that UserControl, the Form have to be hide.
I've overrided these events, in my UserControl class :
public partial class TreatedMetricsDisplay : UserControl
{
private TreatedMetricsWindow _treatedMetricsWindow;
public TreatedMetricsDisplay()
{
InitializeComponent();
_treatedMetricsWindow = new TreatedMetricsWindow ();
}
protected override void OnMouseHover(EventArgs e)
{
base.OnMouseHover (e);
this._treatedMetricsWindow.Show();
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave (e);
this._treatedMetricsWindow.Close ();
}
}
No exceptions, but events are not called.
Showing the Form will steal the Focus, It will get activated. So, it isn't a good idea to show Form as ToolTip. It will not behave as one would expect.
You need to use ToolStripDropDown combined with ToolStripControlHost. Which makes it possible to show any control as tooltip(not exactly).
public partial class MainForm : Form
{
private ToolStripDropDown dropDown = new ToolStripDropDown();
public MainForm()
{
InitializeComponent();
dropDown.Items.Add(new ToolStripControlHost(new ToolTipUserControl() { Size = new Size(200, 200) }));
}
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
dropDown.Show(MousePosition);
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
dropDown.Hide();
}
}
ToolTipUserControl could be any Control which you want to show as tooltip.
Here is a code sample that might help:
private void ToolTipControl_Load(object sender, EventArgs e)
{
AttachHandlers(this);
}
private void AttachHandlers(Control currentControl)
{
foreach (Control control in currentControl.Controls)
{
control.MouseHover += GenericMouseHover;
control.MouseLeave += GenericMouseLeave;
if (control.Controls.Count != 0)
{
AttachHandlers(control);
}
}
}
void GenericMouseLeave(object sender, EventArgs e)
{
// no need to hide it if there was no form created in first place
if(_form != null && _form.Visible)
Form.Hide();
}
private void GenericMouseHover(object sender, EventArgs e)
{
Form.Location = this.PointToClient(Cursor.Position);
Form.Show();
}
ToolTipForm _form;
private ToolTipForm Form
{
get
{
if (_form == null)
{
_form = new ToolTipForm();
}
return _form;
}
}