Winform hell–running programs with low resolution

I’ve a problem, we developed a winform program that has a really big startup form. The height of the form is 880 pixel and is designed to work on big monitors. Clearly everything is resizable, so if you resize the form you can still work with it, with no problem.

image

Figure 1: the form resized

as you can see the form is divided in: blue part (filters), yellow part (result of the query), and finally red part (detail of selected element). As you can see, even when the form is resized it is still usable. A user that work at 1027×768 send me this screenshot telling me that she could not edit details of the items

image

Figure 2: layout is completely messed, the red part (edit details) is missing

She told me that she is not able to edit records, because the bottom part is missing, so I wonder why this is happening.

The form has a single TabControl, that contains a lot of other controls. It seems that if the form is opened at a resolution that was greater than screen resolution the tabItem will keep the original height, and since it is anchored to up,left,bottom,top, it got messed. Since the Tabcontrol has a height in the designer of 802 pixel, the form gets resizied to 730 but the TabControl still is 802 pixel, and the whole layout got messed up.

The solution is this simple fix.

   1: if (this.Height > Screen.PrimaryScreen.WorkingArea.Height)

   2: {

   3:     this.Height = Screen.PrimaryScreen.WorkingArea.Height;

   4:     tabControl1.Height = this.Height - 78;

   5: }

This is not elegant code, but since the height of the TabControl is 78 pixel less than the heigh of the full form, I simply put a check to verify if the height of the form is greater than the height of the screen. If true I automatically resize both the form and the TabControl accordingly and the problem is solved.

alk.

Hosting a WPF control in Winform with a scrollable Panel

I have a winform project that uses a WPF control for reports. Everything went good, until we need to add more report to a form, and requirements told us to create a scrollbar to show all reports. I simply put a panel with autoscroll = true, and inside it I put a TableLayoutPanel and everything seems to works, excepts that ElementHost used to wrap the WPF controls when scrolled shows a messy interface.

image

Figure 1: Messy interface with WPF report when I scroll the panel

This is due to a bad iteration between the panel and the contained ElementHost, because when the panels scrolls the TableLayoutPanel noone tells the hostControl to refresh. The solution is obvious, handle the Scroll event of the Panel:

   1: private void PanelContainerScroll(object sender, ScrollEventArgs e)

   2: {

   3:    ehReport21111.Refresh();

   4:    ehReport22222.Refresh();

   5:    ehReport3333.Refresh();

   6:    .....

   7: }

I simply manually call Refresh for every ElementHost to force refresh of WPF control hosted in it.

alk.

Force databinding update in Winform without waiting for lose focus

I have a winform application where the user can select an element into a grid, edit its properties, and then decide if the modification should be saved or rejected. Thanks to binding, IEditableObject and INotifyPropertyChanged, writing such feature is a breeze, but the user signaled me that something was wrong. He told me: “suppose you choose an element, then change a value in the combo, nothing happens. When I press tab or move the “Save” and “Cancel” button become visible, but I wanto them to be visible as soon as I press a char into textbox or change a value in the combo.

The user does not like this approach, he explicitly asked me to show “Save” and “Cancel” button when someone change a value and keep them hidden otherwise, but he does not like the need to focus change to make them visible. The solution was really simple, you need to intercept some events to force binding update whenever you want.

private void txtAction_TextChanged(object sender, EventArgs e)
{
    txtAction.DataBindings["Text"].WriteValue();
}

private void cmbActionStatus_SelectedValueChanged(object sender, EventArgs e)
{
    cmbActionStatus.DataBindings["SelectedValue"].WriteValue();
}

With this code I’m simply intercepting the change in the texbox and the change in selected value of the combo, then I can simply use the DataBindings array that each control has, to find the Binding object related to the bound property (Text for textbox, and SelectedValue for combo), and simply call WriteValue() method.

With this code, all my binding logic is aware of change as soon as the user type some char into the textbox or change the value of a combo.

alk.

Tags:

Implement a simple undoable editing with a winform GridView

I need to implement a very simple interface, the user loads from a service a certain amount of objects displayed into a gridview. Then selecting an object in the gridview the user can edit object properties in a detail panel situated under the grid. Specification ask me to

1) avoid that the user update some property of the object, then forget to press “Save” button and move to another object forgetting to update data calling the service update function.

2) the system must support an “undo” button that restore the object properties if the user decides not to save changes.

Since the service return Dto objects that supports INotifyPropertyChanged and IEditableObject everything is a breeze. I used a custom MVC structure in winform, where the view use BindingSource objects to bind control to data, and pass BindingObjects to the controller to interact with current object. The first step is intercepting when the users begins editing of an object.

_bsActionList = View.SetListOfAction(ret);
_bsActionList.CurrentChanged += SelectedActionChanged;
ChangeSelectedActionFromList((ActionDto)_bsActionList.Current);

The controller simply set the list of Action to the view, then save the BindingSource (created by designer) then intercept CurrentChanged and fire the ChangeSelectedActionFromList. My view was designed in this way: when controller call SetLisOfAction it assign the list of actionDto to a bindingSource and then returns it to the caller. LEts see how to react at change of current element.

private void ChangeSelectedActionFromList(ActionDto actual)
{
    if (actual != null)
    {
        actual.PropertyChanged += (sender, e) => View.BeginEditOfAction();
    }
}

This function simply verify if the current object is different from null, if yes it intercept propertyChanged and call BeginEditOfAnAction in the view.

public void BeginEditOfAction()
{
    btn2Update.Visible = true;
    btn2CancelCurrentEdit.Visible = true;
    dgActions.Enabled = false;
}

The view reacts disabling the grid, so the user cannot move from current record until he choose to save or cancel current editing. Here is the two actions of the controller

public void UpdateAction()
{
    if (_bsActionList.Current != null)
    {
        ActionDto dto = (ActionDto)_bsActionList.Current;
        if (ManagementService.UpdateAction(dto))
        {
            View.ShowMessage("UpdateOk");
            _bsActionList.EndEdit();
            View.EndEditOfAction();
        }
    }
}

public void CancelEditOfAction()
{
    _bsActionList.CancelEdit();
    View.EndEditOfAction();
}

Update Action simply calls a service to update the action, if the action is successful it show an UpdateOk message then end the edit of the action and the edit of the BindingSource. If the user press cancel simply cancel edit of binding source. This is because my dto object supports both INotifyPropertyChanged and IEditableObject.

Alk.

Tags: