Location>code7788 >text

WPF] Command's use of some programs

Popularity:569 ℃/2024-07-28 12:22:49

Command, or command, specifically, refers to an object that implements the ICommand interface. This interface requires that implementations include these members:

1, CanExecute method: determine whether the command can be executed, if so, return true; if not, return false;

2、CanExecuteChanged event: the control that sends the command (command source) can subscribe to this event to be notified when the executability of the command changes;

3、Execute method: This method is called when executing a command. You can write the command logic in this method.

Command Source (ICommandSource)

The control that sends the command is the command source, e.g. common menu items, buttons etc. That is, how the command is triggered, which is definitely related to user interaction. Controls with no interaction are generally not required to send commands. If there is a command source, there will be a command target. If the command source is the sender, then the command target is the receiver of the command (who the command will eventually work on). For example, clicking the K button clears the text in the T control. Then, K is the command source, T is the command target. This is an example of large partners believe that they can understand, the old week will not say too much, the theory part of the simpler the better to understand. There is nothing mysterious here, as long as you distinguish the role on the line, who issued, who receives.

The command must have a trigger, so the source is required, and, the control that is the source of the command implements the ICommandSource interface and implements three members:

1. Command: The command object to be sent;

2, CommandParameter: command parameters. This is an arbitrary object, by you to decide what it is, for example, your command is to delete the data records of an employee, then, this parameter may be the employee ID. this parameter is optional, when your command logic requires additional data is used, do not have to default to null on it;

3, CommandTarget: target. Which control the command will act on. In fact, this is also optional, the command can be no target control. For example, to delete an employee record, if you know which record to delete, then there is no need to target controls. Of course, if your logic is to clear the text of the text box, then the target control is TextBox, depending on the logic of your code.

Controls like Button and MenuItem, which are command sources, implement the ICommandSource interface.

command logic

Command logic is the work that your commands are going to do. Let's do a demo.

The following example will delete a student record with the command. the Student class is defined as follows:

    public class Student
    {
        public string? Name { get; set; } = string.Empty;
        public int ID { get; set; }
        public int Age { get; set; }
        public string Major { get; set; } = string.Empty;
    }

    public class StudentViewManager
    {
        private static readonly ObservableCollection<Student> _students = new ObservableCollection<Student>();

        static StudentViewManager()
        {
            _students.Add(new Student()
            {
                ID = 1,
                Name = "Xiao Chen (1905-1992), female revolutionary and martyr",
                Age = 20,
                Major = "Tiger Fighting Specialty"
            });
            _students.Add(new Student()
            {
                ID = 2,
                Name = "Zhang Xiaozhang (1908-1992), Mao *'s second wife",
                Age = 21,
                Major = "Tiling specialties"
            });
            _students.Add(new Student()
            {
                ID = 3,
                Name = "Lü Bu (-198), general and warlord",
                Age = 23,
                Major = "person who specializes in pitting one's righteous father against another"
            });
        }

        public static ObservableCollection<Student> Students
        {
            get { return _students; }
        }
    }

Then, define a class that implements the ICommand interface.

    public class DelStuCommand : ICommand
    {
        public event EventHandler? CanExecuteChanged;

        public bool CanExecute(object? parameter)
        {
            return !( == 0);
        }

        public void Execute(object? parameter)
        {
            Student? s = parameter as Student;
            if (s == null)
                return;

            (s);
        }
    }

Executing this command requires parameters so that it knows which student record to delete.

In the following XAML, the ListBox control displays a list of students, and the buttons reference the above command objects.

    <Grid>
        <>
            <RowDefinition/>
            <RowDefinition Height="auto"/>
        </>
        <>
            <local:DelStuCommand x:Key="cmd"/>
        </>
        <Button Content= "Delete" ="1" Command="{StaticResource cmd}"
                CommandParameter="{Binding ElementName=tc, Path=SelectedItem}"/>
        <ListBox x:Name="tc" ="0">
            <>
                <DataTemplate DataType="local:Student">
                    <TextBlock>
                        <Run Text="{Binding Name}"/>
                        <Span> | </Span>
                        <Run Text="{Binding Major}" Foreground="Blue"/>
                    </TextBlock>
                </DataTemplate>
            </>
        </ListBox>               
    </Grid>

The Button class implements the ICommandSource interface and specifies the parameters to be passed to the command through the CommandParameter property.

After running the program, select an item in the ListBox and click the "Delete" button.

After deletion, only two records remain. Repeat the following, and when all records have been deleted, the "Delete" button will be disabled.

As you can learn from this example, commands can encapsulate a certain behavior into a single whole. This increases its reusability; buttons, menus, and toolbar buttons can all use the same command for the same function.

Routing Commands and CommandBinding

Implementing the ICommand interface is simple and easy to use, but it has a problem: if I have a lot of command logic in my program, then I have to define a lot of command classes. For example, like this, wouldn't you have to define dozens of command classes.

This leads to the use of the RoutedCommand class.

The RoutedCommand class implements the ICommand interface, which encapsulates some general logic, and the specific logic will be handled in the way of events.The events of the RoutedCommand class all come from the routing (tunneling) events registered by the CommandManager class. That is

1, CanExecute and PreviewCanExecute event: when you want to determine whether the command can be executed when the event will occur. Preview begins with the tunnel event. Maybe some big partners do not remember this term. In fact, routing events and tunneling events are essentially the same, only the direction of delivery is different. When you dig a tunnel, do you drill from the outside to the inside? So, tunnel events are propagated from the outer element to the inner one, while route events are propagated from the inside to the outside.

2, Executed and PreviewExecuted events: we can handle this event, and then write the command logic you want to implement.

As you can see, with RoutedCommand, we don't need to define a bunch of command classes, but use them all, with the code logic written in the Executed event. This includes the RoutedUICommand command, which just has an extra Text property to specify the associated text, which will be displayed on the menu.

However, we won't handle the events of the RoutedCommand class directly, but use it with another class, CommandBinding. With it, events can bubble (or sink), that is, they can propagate up or down. The propagation path starts from the Command Target and ends with the CommandBindings that catch the event. It doesn't matter if you don't understand this, we'll illustrate it with an example later.

 

Let's do another example. In this example, we use four menu items to change the color of the rectangle.

Since we are now using the RoutedCommand class, we don't need to define the command class anymore, so we can declare the command directly in the resource in the XAML document.

    <>
        <!--command list-->
        <RoutedCommand x:Key="greenCmd" />
        <RoutedCommand x:Key="silverCmd" />
        <RoutedCommand x:Key="redCmd" />
        <RoutedCommand x:Key="blackCmd" />
    </>

We define a set of menus, and a rectangle.

    <Grid>
        <>
            <RowDefinition Height="auto"/>
            <RowDefinition/>
        </>
        <Menu>
            <MenuItem Header= "Color">
                <MenuItem Header= "Green" Command="{StaticResource greenCmd}" CommandTarget="{Binding ElementName=rect}"/>
                <MenuItem Header= "Silver" Command="{StaticResource silverCmd}" CommandTarget="{Binding ElementName=rect}"/>
                <MenuItem Header= "Red" Command="{StaticResource redCmd}" CommandTarget="{Binding ElementName=rect}"/>
                <MenuItem Header= "Black" Command="{StaticResource blackCmd}" CommandTarget="{Binding ElementName=rect}"/>
            </MenuItem>
        </Menu>
        <Rectangle ="1" Height="80" Width="100" Name="rect" Fill="Blue" />
    </Grid>

The grid is divided into two rows, with menus on top and rectangles on the bottom. The Command property of each menu item already references the desired command object. the CommandTarget property references the rectangle object by binding. Note here that Target requires a type that implements the IInputElement interface. As you can see, not all objects can act as targets. the Rectangle class can act as a command target.

Instead of handling the events of the RoutedCommand class directly, we need to use the CommandBinding. All the subclasses of UIElement inherit the CommandBindings collection, so don't worry, most of the interface elements can be used. In this example, we write the CommandBinding on the Grid.

    <Grid>
        <>
            <RowDefinition Height="auto"/>
            <RowDefinition/>
        </>
        <>
            <CommandBinding Command="{StaticResource greenCmd}" 
                                CanExecute="OnRectCanExecut"
                                Executed="OnGreenCmdExe"/>
            <CommandBinding Command="{StaticResource silverCmd}"
                                CanExecute="OnRectCanExecut"
                                Executed="OnSilverCmdExe"/>
            <CommandBinding Command="{StaticResource redCmd}"
                                CanExecute="OnRectCanExecut"
                                Executed="OnRedCmdExe"/>
            <CommandBinding Command="{StaticResource blackCmd}"
                                CanExecute="OnRectCanExecut"
                                Executed="OnBlackCmdExe" />
        </>
        <Menu>
            ……
            </MenuItem>
        </Menu>
        <Rectangle ="1" Height="80" Width="100" Name="rect" Fill="Blue" />
    </Grid>

When using CommandBinding, pay attention to the command referenced by the Command you want to use, here is to be consistent with the command referenced by the four menu items, otherwise, the CanExecute and Executed events do not work (the command can not be triggered correctly). If the event logic is the same, you can share a handler, such as the above, the CanExecute event shares a common handler.

Next, let's deal with the events.

private void OnGreenCmdExe(object sender, ExecutedRoutedEventArgs e)
{
    Rectangle rect = (Rectangle)e.OriginalSource;
     = new SolidColorBrush();
}

private void OnSilverCmdExe(object sender, ExecutedRoutedEventArgs e)
{
    Rectangle rect = (Rectangle);
     = new SolidColorBrush();
}

private void OnRedCmdExe(object sender, ExecutedRoutedEventArgs e)
{
    Rectangle rect = (Rectangle);
     = new SolidColorBrush();
}

private void OnBlackCmdExe(object sender, ExecutedRoutedEventArgs e)
{
    Rectangle rect = (Rectangle);
     = new SolidColorBrush();
}

private void OnRectCanExecut(object sender, CanExecuteRoutedEventArgs e)
{
     = ( != null &&  is Rectangle);
}

In the OnRectCanExecut method, this example determines that the command is allowed to be executed as long as the target of the command is not null and is a rectangular object. The property is used to set a boolean value to indicate whether the command can be executed or not.

The code is very simple, so I'm not going to explain much. The point is that the source of these events is the Command Target, which is the Rectangle referenced by the OriginalSource, and the event path bubbles up from the target - in human terms, it's from the Rectangle upwards! It doesn't matter what level of CommandBinding it is, as long as the event matches the command, it will be triggered.

We might want to change this by moving the last two CommandBindings under the Grid up to the Window object.

    <>
        <CommandBinding Command="{StaticResource redCmd}"
                                CanExecute="OnRectCanExecut"
                                Executed="OnRedCmdExe"/>
        <CommandBinding Command="{StaticResource blackCmd}"
                                CanExecute="OnRectCanExecut"
                                Executed="OnBlackCmdExe" />
    </>
    <Grid>
        <>
            <RowDefinition Height="auto"/>
            <RowDefinition/>
        </>
        <>
            <CommandBinding Command="{StaticResource greenCmd}" 
                                CanExecute="OnRectCanExecut"
                                Executed="OnGreenCmdExe"/>
            <CommandBinding Command="{StaticResource silverCmd}"
                                CanExecute="OnRectCanExecut"
                                Executed="OnSilverCmdExe"/>
        </>
        <Menu>
            ……
        </Menu>
        ……
    </Grid>

After running it, you'll see that all four menus work.

Starting with the Rectangle and bubbling upwards, we first find two CommandBindings on the Grid element, which match and are used, and then further up, we find two more on the Window element, which match and are used. So, in the end, all four work. So the route starts with the Rectangle and bubbles up to the Window object.

In fact, the above Executed events can be combined and handled in a single method, as long as the CommandParameter is used to distinguish which color is used.

 private void OnCmdExecuted(object sender, ExecutedRoutedEventArgs e)
 {
     Rectangle rect = (Rectangle);
     // Getting the value of a parameter
     int val = Convert.ToInt32();
     // Selection of colors according to parameters
     SolidColorBrush brush = new();
     switch (val)
     {
         case 0:
              = ;
             break;
         case 1:
              = ;
             break;
         case 2:
              = ;
             break;
         case 3:
              = ;
             break;
         default:
              = ;
             break;
     }
      = brush;
 }

In the XAML document, replace the event handler set earlier and set the CommandParameter in the menu item.

<CommandBinding Command="{StaticResource redCmd}"
                        CanExecute="OnRectCanExecut"
                        Executed="OnCmdExecuted"/>
<CommandBinding Command="{StaticResource blackCmd}"
                        CanExecute="OnRectCanExecut"
                        Executed="OnCmdExecuted" />
<CommandBinding Command="{StaticResource greenCmd}" 
                    CanExecute="OnRectCanExecut"
                    Executed="OnCmdExecuted"/>
<CommandBinding Command="{StaticResource silverCmd}"
                    CanExecute="OnRectCanExecut"
                    Executed="OnCmdExecuted"/>
<MenuItem Header= "Green" Command="{StaticResource greenCmd}" CommandTarget="{Binding ElementName=rect}" CommandParameter="0"/>
<MenuItem Header= "Silver" Command="{StaticResource silverCmd}" CommandTarget="{Binding ElementName=rect}" CommandParameter="1"/>
<MenuItem Header= "Red" Command="{StaticResource redCmd}" CommandTarget="{Binding ElementName=rect}" CommandParameter="2"/>
<MenuItem Header= "Black" Command="{StaticResource blackCmd}" CommandTarget="{Binding ElementName=rect}" CommandParameter="3"/>

Assigning Shortcut Keys

The benefit of commands is not only the ability to share code logic across multiple sources, but also support for shortcut binding. This is where the InputBinding object comes in. Look closely and see that this class implements the ICommandSource interface.

public class InputBinding : , 

Therefore, it can also be associated with a command, and whenever the InputBinding is triggered, the associated command will be executed. Let's add a shortcut to the above example.

<>
    <KeyBinding Gesture="ctrl+shift+1" 
                    Command="{StaticResource greenCmd}"
                    CommandTarget="{Binding ElementName=rect}"
                    CommandParameter="0"/>
    <KeyBinding Gesture="ctrl+shift+2"
                    Command="{StaticResource silverCmd}"
                    CommandTarget="{Binding ElementName=rect}"
                    CommandParameter="1"/>
    <KeyBinding Gesture="ctrl+shift+3"
                    Command="{StaticResource redCmd}"
                    CommandTarget="{Binding ElementName=rect}"
                    CommandParameter="2"/>
    <KeyBinding Gesture="CTRL+SHIFT+4"
                    Command="{StaticResource blackCmd}"
                    CommandTarget="{Binding ElementName=rect}"
                    CommandParameter="3"/>
</>

Derived classes of the UIElement class inherit the InputBindings collection, and we usually put the InputBinding in the window collection. In fact, we can write the InputBinding in the InputBindings collection. As we mentioned earlier, events bubble up from the Target object, so defining an InputBinding or CommandBinding on the window will capture as many command events as possible.

InputBinding is just a base class, it has two derived classes - KeyBinding and MouseBinding, you don't need to explain them to me, just look at the names and you can guess what they are for. The example uses shortcut keys, so we use KeyBinding. shortcut keys have two declaration methods in XAML:

1. As shown in this example, set the Gesture property directly. Use the string form of the key, not case sensitive, and connect the keys with "+", such as Ctrl + C. This method defines the modifier key and the normal key together, which is convenient and easy to use;

2. Modifiers and keys are defined separately. That is, using Key and Modifiers attributes, Key specifies normal key, such as "G"; Modifiers specifies modifier keys, such as "Ctrl + Alt". Therefore, the shortcut keys in this example can also be defined in this way:

<KeyBinding Modifiers="Ctrl+Shift"
            Key="D4"
                Command="{StaticResource blackCmd}"
                CommandTarget="{Binding ElementName=rect}"
                CommandParameter="3"/>

The Key attribute here is special, you can't write "4" directly, because it can't be converted from the string "4" to Key enumeration, it will report an error, you can specify "D4", D4", "D5" and so on. The numeric keys specified here are the numbers in the large keyboard area (the row above the QWERTYUIOP), not the numeric keys on the right side of the keypad. Use "NumPad4" for the small keypad. The small numeric keypad is invalid when combined with some modifier keys, as tested by Lao Zhou, Shift, Alt, Win are invalid, but Ctrl is OK. Therefore, it is more reliable to use the letter keys, and there is no need to distinguish between large and small keyboard areas.

Key: The Key + Modifiers method and the Gesture method can only be used as one or the other, not at the same time, which will create ambiguity.

Why is CommandTarget optional?

As mentioned earlier, the command target is optional and can be left unspecified, why? It depends on how the command source is handled. We can take a look at WPF's internal handling.

internal static bool CanExecuteCommandSource(ICommandSource commandSource)
{
    ICommand command = ;
    if (command != null)
    {
        object parameter = ;
        IInputElement target = ;

        RoutedCommand routed = command as RoutedCommand;
        if (routed != null)
        {
            if (target == null)
            {
                target = commandSource as IInputElement;
            }
            return (parameter, target);
        }
        else
        {
            return (parameter);
        }
    }

    return false;
}

Triggers the CanExe event if the command is a RoutedCommand and the target exists; if no target is specified, the command source is used as the target.

If the command is not a RoutedCommand, the target is simply ignored.

So, in summary, Target is optional. However, for non-routed commands, the default will be to set theThe control on which the keyboard focus is locatedConsidered a target.

Now, Lao Zhou believes that the big partners will be able to use the command. In practice, you can also encapsulate the command directly into the Model type, for example, as a public property. This is very convenient, especially if you have a number of windows, all of which may have an "Edit Employee Info" menu or button. If you encapsulate the command directly in your employee information model, you can open the edit dialog in the logic of the command. This saves a lot of repetitive code, and the code for the N windows is so clean that you don't even have to handle Click events for the buttons.

----------------------------------------------------------------------------------------------------------------------------------------------

Finally, explain why Lao Zhou has been so inefficient in writing hydrology lately. Because Lao Zhou has recently been introduced by a friend and dispatched to the development department of the headquarters of Group B in the name of an employee of Company A. It is similar to outsourcing or something like that, where he works for a while. It's similar to outsourcing or something like that, that is, to work there for a period of time. It's a complicated relationship. In fact, Lao Zhou originally did not want to go, but still gave his friend 45% of the face (alas, the most pathetic thing is that people always think that face can be eaten), agreed to go, and earn some living expenses. The food is not included, the round trip will use the Internet car. Because this "a period of time" is too vague, rent a house is not good to get, pay a deposit and so on, time and uncertainty, how to organize. So, we have to take a taxi, the cost of finding their company reimbursement.

If you've been out on assignment a lot, you probably know that it's not a good job. Think about it, why do people come to your door? It's because they can't solve the problem themselves, and you used to be in charge of gnawing on the hard core. Due to the signing of a confidentiality agreement, Lao Zhou can not say what the project is. Anyway, the project is very large, TM complex, mainly to help them do optimization. Their office with the market like, every day is very lively, you can walk around at work, chatting about eggs. The atmosphere is good, you wander around the leadership does not care, anyway, you have to complete the progress. Roughly estimated by Lao Zhou, a table sitting 8 people, the office is very large, there are 6 columns and 17 rows, can sit 6 * 17 * 8 people, the whole building has 2,313 people (I heard them say so in the radio), I do not know whether to count our outsourcing staff. Imagine how big their development team is.

After all, it is a large group of companies with many production bases in Southeast Asia and Europe. So their development team was originally set up to develop software systems for the factories of their subsidiaries. However, in the cafeteria to listen to insiders say, in addition to their own group of projects in the past few years, outside of the miscellaneous projects are also connected to a lot of projects, and very chaotic, everyone is working very breathless, often can not distinguish which project with which project. A project also built a lot of branches, many versions. When I first got there, the old week also made the whole very speechless, the project name are [three letters + number + a letter] said, the last letter indicates the version of the branch. The project names are all [three letters + numbers + letters], and the last letter indicates the version branch. Looking at the task documentation, and then looking for the project on the source code server made me dizzy.

Originally, I thought that such a big company should have written the code in a very standardized way. Who ever thought, they are completely "can run on good, other exemptions", the code is really written a mess, even do not know how many hands, look at the comments inside, the earliest there is a record of changes in 2013. And inside the comments are traditional, Chinese, English, Japanese, and other unknown bird language have. At first I thought it was a mess. I guess there are all kinds of Vietnamese. It's really a hybrid code.

Honestly, it's really depressing to be expatriated to someone else's big team. Their own people a circle, like to bully newcomers. Of course, it's not physical bullying, after all, Old Zhou practiced with the jungle crooks for two years when he was a kid, and in a real fight, Old Zhou can fight five. Old Zhou refers to the fact that they always give you some difficult tasks to do - also expected. Therefore, according to Lao Zhou's experience in fooling people for many years, you must learn to "pretend to be confused" when you are assigned to other companies.

What does that mean? It's not that you're told to act like a fool, and to act like a vegetable. You can't act like a god, or they'll throw more hard bones for you to gnaw on (people are not as good as dogs, and dogs don't even gnaw on bones anymore). So you have to act like a rookie, but not too much of one. The sending company will definitely brag about how many centuries of development experience you have when you are introduced, and if you pretend to be too much of a rookie, they will realize that you don't want to work, and are pretending on purpose. Pretend to pretend to be a bit of a dish, not too dish. For example, something Lao Zhou can actually use 30 minutes to do out, I have to do his 2 hours. Originally a day can be completed, have to do a two days. If the manager asks, say "this RadGridView control and WinForm is not compatible, if you change the UI, to deal with 1, 2, 3, 4 ......" In short, there is a great deal of difficulty, and it takes an unpredictable amount of time to complete. If you can get it done in three days, say a week. Dragging out the progress would lighten the load. Because Lao Zhou was tired, taking a cab over to help others with their projects during the day, and going back at night to change two other projects. Brain buzzing during the day, brain sizzling at night.

Slow down to do it, and when the time comes for the dispatch appointment, just flashback. Anyway, a month or two, procrastinate and carry through. There's no need to play hard to get to give people a good impression of you, you'll leave without getting acquainted with them anyway, that food market like office is so big, who remembers you.

That's all the water writing for today, tomorrow it's back to mucking around at the grocery store, and there's still a month and a half to go, so I'll get through it quickly.