0

I'm adding some functionality to the scrollwheel event on a Chart control and I was curious about the bitwise AND that is present in most documentation when determining which modifier key was pressed (e.g. https://msdn.microsoft.com/en-us/library/yazd4ct6(v=vs.110).aspx)

void Chart_MouseWheel(object sender, MouseEventArgs e)
{
    //if ((ModifierKeys & (Keys.Shift | Keys.Control)) == (Keys.Shift | Keys.Control)) 
    if(ModifierKeys == (Keys.Shift | Keys.Control))
    {
        //+/- 5 chart threshold with control and shift held
        Threshold += (decimal)(e.Delta / 24m / 100m);
    }
    //else if ((ModifierKeys & Keys.Shift) == Keys.Shift)
    else if(ModifierKeys == Keys.Shift)
    {
        //+/- 1 chart threshold change with shift held
        Threshold += (decimal)(e.Delta / 120m / 100m);
    }
    //else if ((ModifierKeys & Keys.Control) == Keys.Control)
    else if(ModifierKeys == Keys.Control)
    {
        var selectedIndex = styledComboBox1.SelectedIndex;
        selectedIndex += -1*(e.Delta / 120);

        if (selectedIndex < 0) selectedIndex = 0;
        if (selectedIndex > (styledComboBox1.Items.Count - 1)) selectedIndex = styledComboBox1.Items.Count - 1;

        styledComboBox1.SelectedIndex = selectedIndex;
    }
    else
    {
        AxisMax += 0.02 * e.Delta / 120;
    }
}

Both the commented and uncommented if/else if lines produce the same result with each of the three combinations (Shift+Control, Shift, Control) producing the desired effects so I'm just wondering why it works in the uncommented scenario.

Additionally, when I wasn't checking for Shift+Control as the first condition, it would just fall into the Shift block - why is that?

Anthony
  • 6,422
  • 2
  • 17
  • 34

2 Answers2

1

The commented code works in the same way of uncommented one as long as you don't press any other ModifierKeys: try to press also ALT and you should note the difference.

if you skip the first check, the second check in uncommented code say "if only shift is pressed", while the commented one say "if shift is pressed, no matter if other modifiers are pressed"

So, skipping the first check, pressing CTRL + SHIFT don't match your uncommented one, but match the commented check

Gian Paolo
  • 4,161
  • 4
  • 16
  • 34
  • with the current uncommented approach (i.e. no bitwise AND), adding ALT to any of the combinations results in it falling through to the else block, but switching to the bitwise AND, adding ALT to each combination still causes them to trigger (i.e. CTRL+SHIFT+ALT, SHIFT+ALT and CTRL+ALT) - isn't it better if it only matches the exact keys pressed? – Anthony Dec 11 '15 at 15:39
  • @Worker, I agree with you if click results to X, CTRL click results to Y, CTRL ALT click should not result X to me. As long as you correctly understand the way bitwise OR and AND work, you can implement whatever behaviour you prefer. (exact match, or whatever) – Gian Paolo Dec 11 '15 at 16:09
1

I had some fun with this one, good question!

TL;DR the bitwise AND removes any other flags from the enum, in case you want to ignore them and only find out if the keys you specified are pressed.

Below is a console app that shows you the bits for each operation, and why it works the same with and without the bitwise AND if you're pressing CTRL + SHIFT. The bitwise AND is there to filter out any other modifiers: it makes the check compare to CTRL + SHIFT regardless if ALT or WINDOWS is pressed. There are use cases for each (you might just want to ignore the alt key, which is fine and where you would use it, or you might want to make sure that it is NOT pressed, only CTRL and SHIFT).

The ModifierKeys definition I took from right from the source.

public class Program
{
    [Flags]
    private enum ModifierKeys
    {
        None = 0,
        Alt = 1,
        Control = 2,
        Shift = 4,
        Windows = 8
    }

    static void Main(string[] args)
    {
        ModifierKeys none = ModifierKeys.None;
        ModifierKeys alt = ModifierKeys.Alt;
        ModifierKeys control = ModifierKeys.Control;
        ModifierKeys shift = ModifierKeys.Shift;
        ModifierKeys windows = ModifierKeys.Windows;
        ShowEnums(none, alt, control, shift, windows);

        ShowAddingFlags(control, shift);

        ShowBitwiseAnd(none, alt, control, shift, windows);


        CompareWithShiftAndControl(alt, control, shift);

        Console.ReadKey();
    }

    private static void ShowBitwiseAnd(params ModifierKeys[] ms)
    {
        List<ModifierKeys> mods = new List<ModifierKeys>();
        mods.AddRange(ms);

        ModifierKeys c = ModifierKeys.None;

        Console.WriteLine("Adding all...");

        foreach (var m in mods)
        {
            Console.WriteLine(GetBinaryString(m));
            c = c | m;
        }

        Console.WriteLine(GetBinaryString(c));
        Console.WriteLine(c);
        Console.WriteLine();
    }

    private static void CompareWithShiftAndControl(params ModifierKeys[] ms)
    {
        var withAlt = ModifierKeys.Alt | ModifierKeys.Control | ModifierKeys.Shift;
        var withoutAlt = ModifierKeys.Control | ModifierKeys.Shift;

        Console.WriteLine("Using bitwise And:" );

        var formatter = "{0} & {1} = {2}";
        Console.WriteLine(formatter, GetBinaryString(withAlt), GetBinaryString(withoutAlt), GetBinaryString(withAlt & withoutAlt));
        Console.WriteLine(formatter, withAlt, withoutAlt, withAlt & withoutAlt);

    }

    private static void ShowAddingFlags(params ModifierKeys[] ms)
    {
        List<ModifierKeys> mods = new List<ModifierKeys>();
        mods.AddRange(ms);

        ModifierKeys c = ModifierKeys.None;

        Console.WriteLine("Adding Control and Shift...");

        foreach (var m in mods)
        {
            Console.WriteLine(GetBinaryString(m));
            c = c | m;
        }

        Console.WriteLine(GetBinaryString(c));
        Console.WriteLine(c);
        Console.WriteLine();
    }

    private static string GetBinaryString(ModifierKeys modifierKeys)
    {
        return Convert.ToString((int)modifierKeys, 2).PadLeft(8, '0'); 
    }

    private static void ShowEnums(params ModifierKeys[] ms)
    {
        List<ModifierKeys> mods = new List<ModifierKeys>();
        mods.AddRange(ms);

        foreach (var m in mods)
        {
            Console.WriteLine(m);
            Console.WriteLine(GetBinaryString(m));
        }

        Console.WriteLine();
    }
}
DrewJordan
  • 5,266
  • 1
  • 25
  • 39
  • So basically if you want to allow other keys to be pressed while your keys of interest are pressed, you use the bitwise AND. But if you only want to match a specific key you use the direct comparison – Anthony Dec 11 '15 at 15:56
  • Exactly. The bitwise AND will strip out any bits that are set in one, but not the other, before doing the compare. So, to answer your question, it works in the uncommented scenario because that's the only two keys you're pressing. Add in ALT or the WINDOWS key and it won't work anymore. – DrewJordan Dec 11 '15 at 15:59
  • thanks @DrewJordan - that is the type of behavior I want i.e. exactly matching – Anthony Dec 11 '15 at 16:08