WF4 refresh workflow designer - c#

I have succeeded to reestablish the link between two activities after another one ( which existed before between them) was deleted.
if (containerAsFlowchart != null)
{
for (int i = 0; i < containerAsFlowchart.Nodes.Count; i++)
{
if (containerAsFlowchart.Nodes[i] is FlowStep)
{
FlowStep fs = containerAsFlowchart.Nodes[i] as FlowStep;
if (fs.Next == null)
{
if (i < containerAsFlowchart.Nodes.Count - 1)
{
fs.Next = (FlowNode)((containerAsFlowchart.Nodes[i + 1] as FlowStep));
}
}
}
}
}
Al works fine till now, but even if the connection is made back, I am not able to visualize it in the workflow designer. If I extend or collapse an activity, or any other operation which refreshes the workflow, that pretty arrow is back there but.. is there any way to do this programmatically, and trigger this repainting straight after I delete one activity ?

For the changes to be visible on the designer you've to directly edit its ModelItem.
var modelItem = Designer.Context.Services.GetService<ModelService>().Root;
// Do changes through modelItem ...
For example, to change the DisplayName of the root activity:
modelItem.Properties["DisplayName"].Value = "New Name";
What ModelItem does is to keep every part of the workflow in an agnostic model structure, from complex types down to a simple integer. That model is use by the designer itself to print the workflow to the screen among other things (read arguments, variables, etc.).
Use the debugger and watch the model to learn more.

Related

Weird SharePoint ItemUpdating Behavior

I have a SharePoint list where I register a custom ItemUpdating event receiver but I am seeing some really strange behavior in this solution. This behavior occurs if I add any code to the event receiver other than base.ItemUpdating.
What happens is if I debug the event receiver I can see that properties.AfterProperties has all the values entered on the field and properties.ListItem has the original item. But once the ER finishes running and the page reloads nothing is saved and it just returns to what it was before I changed values. Even more weird, if I go and manually set the after properties similar to below it works and the updates are saved correctly. So basically the event receiver is making me responsible to do any changes to the item but this is not normal behavior for ItemUpdating. Does anyone have any idea what might cause this?
public override void ItemUpdating(SPItemEventProperties properties)
{
var recurringBefore = properties.ListItem.TryGetValue<bool>(Constants.CommonFields.Recurring_STATIC);
var recurringAfter = Convert.ToBoolean(properties.AfterProperties[Constants.CommonFields.Recurring_STATIC]);
//This loop is the horrible fix I have done to manually update the relevant fields but this shouldn't be necessary
var item = properties.ListItem;
foreach (SPField key in item.Fields)
{
if (item[key.InternalName] != properties.AfterProperties[key.InternalName] && key.CanBeDisplayedInEditForm && properties.AfterProperties[key.InternalName] != null)
{
//looping through and setting the AfterProperties to what they already are makes them save? If I don't do this nothing saves
properties.AfterProperties[key.InternalName] = properties.AfterProperties[key.InternalName].ToString();
}
}
if (!recurringBefore && recurringAfter &&
currWfStatus == Constants.WorkflowStatus.Processed)
{
//do some stuff
}
base.ItemUpdating(properties);
}
Is it because you are not saving current item at all, something like this:
item.update();

Cannot edit DataGridView cell, after choosing option I have to press Esc and lose the edit

So here is the story - I have 2 DataGridViews - one of them is used as a display, the other one is user for editing or adding new entries. I don't use bindings on the edit one. When I am creating new entry I am just extracting the cells' values and passing them to an object. When I am editing though comes the problem.
The edit consists of 2 parts - one is to select the entry to be edited and display it on the second DGV. This is done with SelectionChanged event and the code is bellow. Then when editing is done it should just publish the new task the same way as creation. The thing is that when I am doing the edit and I come to the moment when I have to select from one of my cells - a ComboBoxCell - and I make a selection everything freezes. I cannot click anything else than this ComboBox until I press Esc - which obviously reverts the choice.
Why is this freeze happening and what causes it only when the data is cloned from the first DGV, but is okay when creating a brand new row? I am using VS2012 by the way and this is a windows forms application
Here is the code for duplication of the selected task - this method is the only thing in the DGV1's SelectionChanged Event handler:
public void createTemplateTaskToBeEditted(Form1 form1, ApplicationControl appControl)
{
form1.dg_templateView.Rows.Clear();
for (int i = 0; i < form1.dg_taskView.SelectedRows.Count; i++)
{
if (form1.dg_taskView.SelectedRows[i].Cells[0].Value == null)
{
form1.dg_taskView.SelectedRows[i].Cells[0].Value = false;
}
int index = form1.dg_templateView.Rows.Add();
form1.dg_templateView.Rows[index].Cells[0].Value =
form1.dg_taskView.SelectedRows[i].Cells[0].Value.ToString();
form1.dg_templateView.Rows[index].Cells[1].Value =
form1.dg_taskView.SelectedRows[i].Cells[2].Value.ToString();
form1.dg_templateView.Rows[index].Cells[2].Value =
form1.dg_taskView.SelectedRows[i].Cells[3].Value.ToString();
form1.dg_templateView.Rows[index].Cells[3].ValueType = typeof(ComboBox);
form1.dg_templateView.Rows[index].Cells[3].Value =
form1.PopulateAssignToComboBox(appControl.GetAllowedMembers(form1));
form1.dg_templateView.Rows[index].Cells[4].Value =
form1.dg_taskView.SelectedRows[i].Cells[5].Value.ToString();
form1.dg_templateView.Rows[index].Cells[5].Value =
form1.dg_taskView.SelectedRows[i].Cells[6].Value.ToString();
form1.dg_templateView.Rows[index].Cells[6].Value =
form1.dg_taskView.SelectedRows[i].Cells[7].Value.ToString();
form1.dg_templateView.Rows[index].Cells[7].Value =
form1.dg_taskView.SelectedRows[i].Cells[8].Value.ToString();
form1.dg_templateView.Rows[index].Cells[8].Value =
form1.dg_taskView.SelectedRows[i].Cells[9].Value.ToString();
form1.dg_templateView.Rows[index].Cells[9].Value =
form1.dg_taskView.SelectedRows[i].Cells[10].Value.ToString();
form1.dg_templateView.Rows[index].Cells[10].Value =
form1.dg_taskView.SelectedRows[i].Cells[11].Value.ToString();
form1.dg_templateView.Rows[index].Cells[11].Value =
form1.dg_taskView.SelectedRows[i].Cells[12].Value.ToString();
}
}

FastColoredTextbox AutoWordSelection?

FastColoredTextbox is an user-control that can be downloaded in this url, it looks like this:
Its an amazing control but only can select one word when doubleclicking on the text, can't hold the mouse to select more words, so it only selects the entire current word over the mouse pointer even if you try to move the mouse cursor to left or right to select more text.
I have not found any information explaining the problem, and all of the official example projects has this problem.
Nobody means how to make an AutoWordSelection equivalent of a default TextBox for a FastcoloredTextbox control, but even the most important thing is:
How to select just more than one word with the mouse?
UPDATE:
#mostruash answer is very instructive but in all this time I could not carry out the modifications by myself.
I need a huge help from a C# programmer to carry out this task, my knowledge of C# is very low and the modifications that I made to the source did not work (don't compiled), I went back to the original user-control source to not end up spoiling more. I hate to say this but this time I need the job done, this source is too much for me.
If I'm requesting for too much then maybe with the necesary extended instructions of a C# developer, explaining how to accomplish this step by step, maybe I could carry it out by myself.
UPDATE
A video that demostrates the problem:
https://www.youtube.com/watch?v=Cs2Sh2tMvII
UPDATE
Another demo, I show what the FastColoredTextBox can't do but I would like to do like every other text-editor can do:
I've checked the source code of the project. Dragging is cancelled if a double click occurs and SelectWord is called.
You could modify the source code to include the feature that you request. (https://github.com/PavelTorgashov/FastColoredTextBox). In that case:
You must trace selections that start with double clicks.
Instead of calling SelectWord function, use the Selection class and draggedRange attribute to mark the selected word in OnMouseMove.
You also must handle deselection of words in OnMouseMove.
You must also select spaces between words in OnMouseMove.
The double click is handled in the code piece below:
if (!isLineSelect)
{
var p = PointToPlace(e.Location);
if (e.Clicks == 2)
{
mouseIsDrag = false; //Here, drag is cancelled.
mouseIsDragDrop = false;
draggedRange = null; //Drag range is nullified
SelectWord(p); //SelectWord is called to mark the word
return;
}
if (Selection.IsEmpty || !Selection.Contains(p) || this[p.iLine].Count <= p.iChar || ReadOnly)
OnMouseClickText(e);
else
{
mouseIsDragDrop = true;
mouseIsDrag = false;
}
}
EDIT:
This may require a lot of work to accomplish. So maybe you should use another tool/library. I have not studied the whole source code so there will probably be additional steps to those provided above.
For example, to trace double clicks you can do the following:
Define a class variable/property in FastColoredTextbox.cs: bool isDoubleClick.
Set it to true in OnMouseDown under if(e.Clicks == 2) condition. Set it to false in all other conditions.
Set it to false in OnMouseClick or OnMouseUp or in other relevant mouse event handlers.
That way you will know if series of mouse events had started with a double click event or not. Then you would act accordingly in OnMouseMove because that is where you (un)mark characters or (un)mark words.
LAST WORDS OF CAUTION:
The author of that project did not include any inline comments or any other means of documentation so you will be studying the code line by line to understand what each function/part does.
Add the following statement between Line 5276 and line 5277 in the class FastColoredTextBox.cs:
SelectWord(p);
mouseIsDrag = true; // here
return;
Note that implementing the ultimate behavior would require a good bunch of coding. Whereas the workaround mentioned above might satisfy your needs.
As #mostruash points out in his answer, that is the place where author cancels the mouse drag. Not sure why he deliberately prevents this feature. Only he knows.
if (e.Clicks == 2)//Line 5270
{
mouseIsDrag = false;
mouseIsDragDrop = false;
draggedRange = null;
SelectWord(p);
return;
}
I didn't read whole code, and I have no reason to do it. I just checked quickly and removed them. And it works as you expect.
if (e.Clicks == 2)//Line 5270
{
//Comment or remove completely.
//mouseIsDrag = false;
//mouseIsDragDrop = false;
//draggedRange = null;
SelectWord(p);
return;
}
Note: Am not sure this breaks something else, I've not tested. At least that works. Test it yourself.
My solution is a bit tweaky, but seems to work at first glance.
You have to make some changes in the Code:
Add mouseIsWholeWordSelection flag and a Range variable which can store the initial selected range after double click (best after line 100, I guess):
private bool mouseIsWholeWordSelection;
private Range mouseIsWholeWordSelectionBaseRange;
Change the selection code for double click event as stated above and extend it a bit (line 5222):
if (e.Clicks == 2)
{
//mouseIsDrag = false;
mouseIsDragDrop = false;
mouseIsWholeWordSelection = true;
//draggedRange = null;
SelectWord(p);
mouseIsWholeWordSelectionBaseRange = Selection.Clone();
return;
}
Add evaluation of dragging event for recreating selection (line 5566):
else if (place != Selection.Start)
{
if (mouseIsWholeWordSelection)
{
Selection.BeginUpdate();
var oldSelection = Selection.Clone();
SelectWord(place);
if (Selection.End >= mouseIsWholeWordSelectionBaseRange.End)
{
Selection.Start = (mouseIsWholeWordSelectionBaseRange.Start > Selection.Start) ? mouseIsWholeWordSelectionBaseRange.Start : Selection.Start;
Selection.End = mouseIsWholeWordSelectionBaseRange.End;
}
else if (Selection.Start < mouseIsWholeWordSelectionBaseRange.End)
{
Selection.Start = new Place(Selection.End.iChar, Selection.End.iLine);
Selection.End = mouseIsWholeWordSelectionBaseRange.Start;
}
Selection.EndUpdate();
DoCaretVisible();
Invalidate();
}
else
{
Place oldEnd = Selection.End;
Selection.BeginUpdate();
if (Selection.ColumnSelectionMode)
{
Selection.Start = place;
Selection.ColumnSelectionMode = true;
}
else
Selection.Start = place;
Selection.End = oldEnd;
Selection.EndUpdate();
DoCaretVisible();
Invalidate();
}
return;
}
Add at every place where isMouseDrag is being set to false:
isMouseWholeWordSelection = false;
And there you go.

Why does MonoTouch.Dialog use public fields for some Element options, and public properties for others

I am trying to get a StringElement's 'Value' to update in the UI when I set it after already setting up the DVC.
e.g:
public partial class TestDialog : DialogViewController
{
public TestDialog() : base (UITableViewStyle.Grouped, null)
{
var stringElement = new StringElement("Hola");
stringElement.Value = "0 Taps";
int tapCount = 0;
stringElement.Tapped += () => stringElement.Value = ++tapCount + " Taps";
Root = new RootElement("TestDialog")
{
new Section("First Section")
{
stringElement,
},
};
}
}
However the StringElement.Value is just a public field, and is only written to the UICell during initialization when Element.GetCell is called.
Why isn't it a property, with logic in the setter to update the UICell (like the majority of Elements, e.g. EntryElement.Value):
public string Value
{
get { return val; }
set
{
val = value;
if (entry != null)
entry.Text = value;
}
}
EDIT :
I made my own version of StringElement, derived from Element (basically just copied the source code from here verbatim)
I then changed it to take a class scoped reference to the cell created in GetCell, rather than function scoped. Then changed the Value field to a property:
public string Value
{
get { return val; }
set
{
val = value;
if (cell != null)
{
// (The below is copied direct from GetCell)
// The check is needed because the cell might have been recycled.
if (cell.DetailTextLabel != null)
cell.DetailTextLabel.Text = Value == null ? "" : Value;
}
}
}
It works in initial testing. However I am not sure on whether taking a reference to the cell is allowed, none of the other elements seem to do it (they only take references to control's placed within the cells). Is it possible that multiple 'live'* cell's are created based on the one MonoTouch.Dialog.Element instance?
*I say live to indicate cells currently part of the active UI. I did notice when navigating back to the dialog from a child dialog the GetCell method is invoked again and a new cell created based on the Element, but this is still a 1-1 between the element and the live cell.
For the main question:
Why does MonoTouch.Dialog use public fields for some Element options, and public properties for others?
I've been through the code, and I don't think there's a consistent reason for use of either.
The Dialog project was not part of the MonoTouch project initially - I don't think Miguel knew how useful it was going to turn out when he started wrote and grew it - I think he was more focussed on writing other apps like TweetStation at the time.
I know of several people (including me!) who have branched the code and adapted it for their purposes. I would guess at some future point Xamarin might write a 2.0 version with stricter coding standards.
Taking references to live cells
For limited use you can do this... but in general don't.
The idea of the table view is that cells get reused when the user scrolls up and down - especially in order to save memory and ui resources. Because of this is a long list, multiple elements might get references to the same cell.
If you do want to cache a cell reference then you probably should override GetCell() so that it never tries to reuse existing cells (never calls DequeueReusableCell)
Alternatively, you could try to change some code in the base Element class in order to find out if the Element has a current attached cell - this is what CurrentAttachedCell does in my branch of Dialog https://github.com/slodge/MvvmCross/blob/master/Cirrious/Cirrious.MvvmCross.Dialog/Dialog/Elements/Element.cs (but that branch has other added functions and dependencies so you probably won't want to use it for this current work!)

C# - Avoid duplicating logic between WinForm and UserControl

I found a question that I belive is what I was looking for, but there were certain things that I was not following in the answers. Therefore, I'd like to ask the question in a different way (Thanks for your patience). Here is the link I am referring to:
How to avoid duplicating logic on two similar WinForms?
OK. I have a dialog that I created. We've got controls for User Input, buttons to display other dialogs (to gain other input), etc. Aesthetically, I prefer the dialog with the controls laid out vertically. Anyhow, I was also thinking of creating a UserControl version of this dialog. This UserControl would have all the same controls, and all the same logic, but the controls would be laid out entirely different (more horizontal, then vertical).
So, I can't just create another (3rd) UserControl that I drop on the orignal form, and on the UserControl I want to create. (This 3rd UserControl would then contain all logic - thus, sharing between the two). I can't do this because of the different layouts.
I have no problem creating the two (Form, UserControl), with the controls laid out differently, but I don't want to 'cut-and-paste' all the logic from one to the other.
This does not seem like a case for MVP, or MVC. My model is the dialog itself. The dialog is intialized with some values, yes, but once initialized the "Model" becomes further User Input (which I then grab when they press the OK button).
Take for example this code (an event for one of my buttons on this dialog):
private void EditQuery_Click(object sender, EventArgs e)
{
try
{
EditQueryParameters();
}
catch (System.Exception ex)
{
// TODO: Write ErrMsg to Log file.
MessageBox.Show("Edit Query Parameters Error:\n\n" + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void EditQueryParameters()
{
if (m_ReportType.QueryScoreDetails && optPickDetail.Checked)
{
// This brings up a different type of dialog
QueryDetails();
return;
}
// DateRange, StartDate, and EndDate are all saved from the last time
// I called this dialog
DateType DtType = new DateType(m_ReportType.DBDateRangeField,
m_DateRange, m_StartDate, m_EndDate);
// StartTime, EndTime too!
TimeType TmType = new TimeType(m_ReportType.DBTimeRangeField,
m_StartTime, m_EndTime);
List<AdvancedFilter> Filters = null;
if (lstAdvancedQuery.Items.Count > 0)
{
Filters = new List<AdvancedFilter>();
}
for (int i = 0; i < lstAdvancedQuery.Items.Count; ++i)
{
Filters.Add((AdvancedFilter)lstAdvancedQuery.Items[i]);
}
// QueryType is also saved from the last time I called QueryBuilder
QueryBuilder QryBuilder = new QueryBuilder(m_ReportType.DBCatalog, m_ReportType.DBTable,
m_QueryType, ref DtType, ref TmType, ref Filters);
// I am using Visual WebGUI, I have to do it this way
QryBuilder.Closed += new EventHandler(QryBuilder_Closed);
QryBuilder.ShowDialog();
}
I mean, I suppose I could have some "logic" class, which exposes something like:
public void EditQueryParameters(ref ReportType RptType, bool PickDetail,
string DateRange, DateTime StartDate, DateTime EndDate,
DateTime StartTime, DateTime EndTime, string QueryType)
{
if (ReportType.QueryScoreDetails && PickDetail)
{
// This brings up a different type of dialog
QueryDetails();
return;
}
DateType DtType = new DateType(ReportType.DBDateRangeField,
DateRange, StartDate, EndDate);
TimeType TmType = new TimeType(ReportType.DBTimeRangeField,
StartTime, EndTime);
// Yikes, more stuff to add to the signature of my method
// Will have to pull this outside the method and pass in Filters
List<AdvancedFilter> Filters = null;
if (lstAdvancedQuery.Items.Count > 0)
{
Filters = new List<AdvancedFilter>();
}
for (int i = 0; i < lstAdvancedQuery.Items.Count; ++i)
{
Filters.Add((AdvancedFilter)lstAdvancedQuery.Items[i]);
}
// QueryType is also saved from the last time I called QueryBuilder
QueryBuilder QryBuilder = new QueryBuilder(ReportType.DBCatalog, ReportType.DBTable,
QueryType, ref DtType, ref TmType, ref Filters);
// I am using Visual WebGUI, I have to do it this way
QryBuilder.Closed += new EventHandler(QryBuilder_Closed);
QryBuilder.ShowDialog();
}
There's a lot of set-up to use this method. I don't know, maybe I'm looking for something more .. "slick"?
On top of that, look at some (not all) of my init code (this is called from constructor or form_Load; it doesn't seem worth it to add this to the logic class, so that's all still "cut and paste" between the two):
private void InitializeUserDefinedTitle()
{
txtUserTitle.Text = m_UserTitle;
}
private void InitializePrintSelectionCriteria()
{
// Print Selection Criteria
chkSelectionCriteria.Checked = m_printSelectionCriteria;
}
private void InitializeTrendBy()
{
cmbTrend.Items.AddRange(Enum.GetNames(typeof(TrendBy)));
cmbTrend.SelectedIndex = (int)m_TrendBy;
cmbTrend.Visible = m_ReportType.TrendVisible;
lblTrend.Visible = m_ReportType.TrendVisible;
}
In summary, the original WinForm is a dialog that is intialized with data (constructor), is displayed to the user for input, when they OK the dialog that data is retrieved (and that data is stored outside the dialog, in member variables, for the next time they call the dialog - this is because we want to show what they last picked/entered).
That type of dialog I just described will also be a user control, and the logic should be shared between the two.
Thanks.
You can make two controls A and B, each containing the same buttons and/or other input controls arranged differently. Controls A and B will have identical properties, and events. The form (or third control) will contain the event handlers that allow the logic to be contained in only one place.
You can display either control A or B using the visible property or by adding one to the container.controls property, the container being the containing form or control.
And, for example, instead of having a handler for button1 in controls A and B that handles the complete logic of the button press, the handlers for button1 in control A and B would just raise an event that will be handled by the container of control A or B.
Instead of encapsulating the logic, I would encapsulate the layout. Use a property of the user control to specify which layout yout want. Then wherever it is (standalone form, one of three instances on the same form, whatever) you access it and specify the layout the same way.
As for how to encapsulate the layout, there are a bunch of possibilities. You could just do it programatically, i.e. write each version of the layout code. (The programamatic version would be cleaner if you used some kind of layout containers, like the Panels in WPF.) You could draw the layout in a designer, and copy the generated code. The different versions of the layout logic could be stuffed into private methods, or encapsulated into objects.

Categories

Resources