Get old text from change event? - apache-flex

Can I somehow find out what was the change in the textfield? I would want to compare the old text with the new text ... the problem is, that I have multiple textAreas in a tab-editor, and all the textAreas are watched by one eventListener. I want to get a value calculated by the next formula:
globalChangeCount += thisTextArea.currentCharacterCount - thisTextArea.oldtCharacterCount
where the globalChangeCount is a value modified by all changes in any of the textAreas.
I am searching for these values through the event variable, but can't seam to find the old text of the textArea.

This may or may not be what you're looking to do:
package
{
import mx.controls.TextArea;
public class CountingTextArea extends TextArea
{
public var staleText : String = "";
[Bindable("textChanged")]
[NonCommittingChangeEvent("change")]
public function get charDiff() : int
{
var diff : int = staleText.length - text.length;
staleText = text;
return diff;
}
public function CountingTextArea()
{
super();
}
}
}
I made it so that you can use it as a source for binding. Instead of subscribing to the event on each TextArea, you can use:
function addWatchers():void
{
ChangeWatcher.watch(countingTextArea1, ["charDiff"], charDiffChangeHandler );
...
ChangeWatcher.watch(countingTextArea5, ["charDiff"], charDiffChangeHandler );
}
With the event handler somewhere too:
function charDiffChangeHandler( event : PropertyChangeEvent ) : void
{
trace(event.currentTarget.charDiff);
// or
trace(event.newValue);
}

You can use event.currentTarget to get a reference to the TextArea that fired the event, and use the focusIn event to execute a function to populate a variable with the old text value.

Maybe you should just subclass the TextArea and create an oldText field variable you update internally after all the external listeners have been notified.

Related

Double Clicking on row and getting respective row values

I want to double click on any row to getting respective row values. It is an .Razor page.
First of all, I would create a component that will inherit from QuickGrid, so you can manage it easier in the future.
// CustomGrid.razor.cs
[CascadingTypeParameter( nameof(TGridItem) )]
public partial class CustomGrid<TGridItem> : QuickGrid<TGridItem>, IAsyncDisposable
{
[Inject]
private IJSRuntime JS { get; set; } // inject service in order to use JS interop
// sometimes it is mandatory to override base class parameters by your own
}
// CustomGrid.razor
#using Microsoft.AspNetCore.Components.QuickGrid // move it into the _Imports.razor
#typeparam TGridItem // QuickGrid is a generic-typed component
#inherits QuickGrid<TGridItem> // show inheritance
<div #ref="#_gridRef"> // HTML reference of current element
<QuickGrid TGridItem="TGridItem"
Items="#Items"
ItemsProvider="#ItemsProvider"
ChildContent="#ChildContent"
Class="#Class"
// more parameters... >
</QuickGrid>
</div>
Since there is no built-in functionality for adding your custom logic into QuickGrid, you will need to use some JavaScript interopability. Read about it more in docs here and here.
We need to declare some local variables in our CustomGrid.razor.cs:
private string? _rowValue; // specifies row value of currently double clicked row
private ElementReference _gridRef; // HTML element reference object that will be passed to JS function
private IJSObjectReference? _module; // JS module, a file that contains our JS functions
private DotNetObjectReference<CustomGrid<TGridItem>>? _objRef; // .NET object reference that will be passed to JS function in order to use its C# methods
And override some of the component lifecycle methods:
protected override void OnInitialized()
{
_objRef = DotNetObjectReference.Create( this ); // creates .NET object reference of current component instance
}
protected override async Task OnAfterRenderAsync( bool firstRender )
{
if( firstRender )
{
_module = await JS.InvokeAsync<IJSObjectReference>( "import", "./js/customGrid.js" ); // creates a reference of our JS module
if( _module is not null )
{
await _module.InvokeVoidAsync( "initialize", _gridRef, _objRef ); // calls our JS function and passes some arguments
}
}
}
Now, you need to create a JS module and a functions that will add desired logic for you on the first render of the CustomGrid component, like this:
// wwwroot/js/customGrid.js
export function initialize(customGrid, dotNetObj) {
if (customGrid) { // check if custom grid element exists
var rowValue;
const rows = customGrid.querySelectorAll('tbody > tr'); // get all rows except the header row
for (let i = 0; i < rows.length; i++) {
rows[i].addEventListener('dblclick', (e) => { // add event listener to current row in the loop
rowValue = e.path[1].innerText; // get innerText of current row in the loop
console.log(rowValue)
updateCurrentRowValue(rowValue, dotNetObj); // function that will return the current row value and refresh the UI
});
}
}
}
function updateCurrentRowValue(rowValue, dotNetObj) {
dotNetObj.invokeMethodAsync("UpdateCurrentRowValue", rowValue); // C# method
}
We're almost done here! If you would try to perform double click on the row, you would see an error in the console stating that CustomGrid does not contain a public method called UpdateCurrentRowValue. Let's add it like this:
[JSInvokable]
public void UpdateCurrentRowValue( string rowValue )
{
_rowValue = rowValue; // assign received rowValue from the JS function to our local _rowValue variable
StateHasChanged(); // force UI refresh
}
Now, all you need to do is to display your _rowValue:
// CustomGrid.razor
<div #ref="#_gridRef">
<QuickGrid TGridItem="TGridItem" . . . /> // collapsed for brevity
<p>Current Row Value: #_rowValue</p>
</div>
You will also need to Dispose your newly created objects of _module and _objRef using IAsyncDisposable.DisposeAsync method:
// CustomGrid.razor.cs
async ValueTask IAsyncDisposable.DisposeAsync()
{
if( _module is not null )
{
await _module.DisposeAsync();
}
_objRef?.Dispose();
}
Usage:
<CustomGrid Items="#people">
<PropertyColumn Property="#(p => p.PersonId)" Sortable="true" />
<PropertyColumn Property="#(p => p.Name)" Sortable="true" />
<PropertyColumn Property="#(p => p.BirthDate)" Format="yyyy-MM-dd" Sortable="true" />
</CustomGrid>
That should work. If you will need any help -- don't hesitate to ask!
Remarks:
This is a basic implementation of your request. It doesn't support scenarios when there are more than 1 grid on the page. It might work, but will be buggy, I guess. For that, you will need to add some more code in JS and CustomGrid code-behind. I didn't add it because it would be too much code in one answer (quite a lot of code came out here anyway).
UPD-1:
Removed custom [Parameter]s to override QuickGrid's ones and added a comment.

How to pass argument from one form to another form in ax 2012

Table has one date field. I have two form name as formA and formB ,formA has textbox and button. formB has grid with date field.
So my question is if I enter date in textbox and clicked the button of formA, entered date should be assign in grid of formB. I added table datasource of both forms. Please help me out on this.
Although behavior described by you seems to be not so standard in terms of AX, I would suggest you to use dialog form as a FormA (rather than regular form). That way you respect best practices and desired behavior is achieved easier.
Create class extending RunBase class with date field:
class FormADialog extends RunBase
{
DialogField fieldDate;
TransDate transDate;
}
Here is how we construct form controls:
protected Object Dialog()
{
Dialog dialog = super();
fieldDate = dialog.addField(extendedTypeStr(TransDate), 'Date');
return dialog;
}
The following method will retrieve values from Dialog:
public boolean getFromDialog()
{
transDate = fieldDate.value();
return super();
}
Processing logic goes here:
public void run()
{
FormBTable formBTable;
ttsbegin;
select firstOnly forUpdate formBTable;
formBTable.Date = transDate;
formBTable.write();
ttscommit;
}
The only missing thing is entry point for dialog class (represents FormA):
public static void main(Args _args)
{
FormADialog formADialog = new FormADialog();
FormDataSource formDataSource;
if (formADialog.prompt())
{
formADialog.run();
// FormB should contain menu item for dialog class for the following code
if (args && args.record() && args.record().dataSource())
{
formDataSource = args.record().dataSource();
formDataSource.research();
}
}
}
Now clicking on dialog button will update grid.
If you insist on use of approach with two regular forms. I will think of linkActive() method at the datasource of the second form. Take a look at
Tutorial Form Dynalink. A record change in the parent form notifies the child form, making it call the linkActive method which in turn calls the executeQuery method at the child table datasource.
Another approach could be as follows.
For passing parameters from one form to another a special class Args is usually used.
Initiator form prepares data for transfer within clicked() method of button control:
void clicked()
{
Args args;
FormRun formRun;
args = new Args();
args.parm(dateField.text());
args.name(formStr(FormB));
formRun = classFactory.formRunClass(args);
formRun.init();
formRun.run();
formRun.wait();
super();
}
Receiving endpoint should listen at init() method of FormB:
public void init()
{
Date passedValue;
super();
// check presence
if (element.args())
{
passedValue = str2Date(element.args().parm(), 123);
}
}
Take a look at axaptapedia.com article to see how we can pass complex set of parameters within custom made class.

Dart Language: observable

I have two table rows at an HTML file. When the first row gets clicked, it changes its styling via classes.add("active_style"). If the second row gets clicked, I would like to clear the first row styling.
I know that I can just write...
querySelector("#first_row_div").classes.clear();
... in order to clear the first row class (and then resetting its style), but in a bigger code I think that observable would be the best fit.
I don't know if observable works for this. But, if it does, how can I do that?
EDIT/UPDATE: I think that the right question is "is there any way to run a function when a variable gets changed?".
Thanks for the help!
You can make a getter/setter for a field and run your function in the setter.
class MyClass {
String _cssClass;
String get cssClass => _cssClass;
set cssClass(String newClass) {
_cssClass = newClass;
updateDom();
}
void updateDom() {
// do important work here
}
}
You can use a model class that extends Observable.
Here you have to call dirtyCheck() to make Observable check for changes and notify listeners.
Dart also offers the ChangeNotifier mixin. Here you don't need to call any method for dirty-checking. When changes are made listeners are invoked.
A simple example I wrote a while ago while examining the functionality
import 'package:observe/observe.dart';
class Notifiable extends Object with ChangeNotifier {
String _input = '';
#reflectable
get input => _input;
#reflectable
set input(val) {
_input = notifyPropertyChange(#input, _input, val + " new");
}
Notifiable() {
this.changes.listen((List<ChangeRecord> record) => record.forEach(print));
}
}
class MyObservable extends Observable {
#observable
String counter = '';
MyObservable() {
this.changes.listen((List<ChangeRecord> record) => record.forEach(print));
}
}
void main() {
var x = new MyObservable();
x.counter = "hallo";
Observable.dirtyCheck();
Notifiable notifiable = new Notifiable();
notifiable.input = 'xxx';
notifiable.input = 'yyy';
}

how to update Visual Studio UI when using DynamicItemStart inside a vsix package

I'm implementing a DynamicItemStart button inside a Menu Controller. I'm loading the dynamic items for this button when Visual Studio starts. Everything is loaded correctly so the initialize method is called an I see all the new items in this Dynamic button. After the package is completely loaded I want to add more items to this Dynamic button, but since the package is already loaded the initialize method is not called again and I cannot see the new items in this Dynamic button. I only see the ones that were loaded when VS started.
Is there any way that I can force the update of this Dynamic button so it shows the new items?. I want to be able to update the VS UI after I added more items but outside the Initialize method.
The implementation I did is very similar to the one showed on this msdn example:
http://msdn.microsoft.com/en-us/library/bb166492.aspx
Does anyone know if an Update of the UI can be done by demand?
Any hints are greatly appreciated.
I finally got this working. The main thing is the implementation of a derived class of OleMenuCommand that implements a new constructor with a Predicate. This predicate is used to check if a new command is a match within the DynamicItemStart button.
public class DynamicItemMenuCommand : OleMenuCommand
{
private Predicate<int> matches;
public DynamicItemMenuCommand(CommandID rootId, Predicate<int> matches, EventHandler invokeHandler, EventHandler beforeQueryStatusHandler)
: base(invokeHandler, null, beforeQueryStatusHandler, rootId)
{
if (matches == null)
{
throw new ArgumentNullException("Matches predicate cannot be null.");
}
this.matches = matches;
}
public override bool DynamicItemMatch(int cmdId)
{
if (this.matches(cmdId))
{
this.MatchedCommandId = cmdId;
return true;
}
this.MatchedCommandId = 0;
return false;
}
}
The above class should be used when adding the commands on execution time. Here's the code that creates the commands
public class ListMenu
{
private int _baselistID = (int)PkgCmdIDList.cmdidMRUList;
private List<IVsDataExplorerConnection> _connectionsList;
public ListMenu(ref OleMenuCommandService mcs)
{
InitMRUMenu(ref mcs);
}
internal void InitMRUMenu(ref OleMenuCommandService mcs)
{
if (mcs != null)
{
//_baselistID has the guid value of the DynamicStartItem
CommandID dynamicItemRootId = new CommandID(GuidList.guidIDEToolbarCmdSet, _baselistID);
DynamicItemMenuCommand dynamicMenuCommand = new DynamicItemMenuCommand(dynamicItemRootId, isValidDynamicItem, OnInvokedDynamicItem, OnBeforeQueryStatusDynamicItem);
mcs.AddCommand(dynamicMenuCommand);
}
}
private bool IsValidDynamicItem(int commandId)
{
return ((commandId - _baselistID) < connectionsCount); // here is the place to put the criteria to add a new command to the dynamic button
}
private void OnInvokedDynamicItem(object sender, EventArgs args)
{
DynamicItemMenuCommand invokedCommand = (DynamicItemMenuCommand)sender;
if (null != invokedCommand)
{
.....
}
}
private void OnBeforeQueryStatusDynamicItem(object sender, EventArgs args)
{
DynamicItemMenuCommand matchedCommand = (DynamicItemMenuCommand)sender;
bool isRootItem = (matchedCommand.MatchedCommandId == 0);
matchedCommand.Enabled = true;
matchedCommand.Visible = true;
int indexForDisplay = (isRootItem ? 0 : (matchedCommand.MatchedCommandId - _baselistID));
matchedCommand.Text = "Text for the command";
matchedCommand.MatchedCommandId = 0;
}
}
I had to review a lot of documentation since it was not very clear how the commands can be added on execution time. So I hope this save some time whoever has to implement anything similar.
The missing piece for me was figuring out how to control the addition of new items.
It took me some time to figure out that the matches predicate (the IsValidDynamicItem method in the sample) controls how many items get added - as long as it returns true, the OnBeforeQueryStatusDynamicItem gets invoked and can set the details (Enabled/Visible/Checked/Text etc.) of the match to be added to the menu.

Binding to a Property that only have get method

I have a question regarding binding in WinRT.
I have a Viewmodel like this:
public class MainPageViewModel : INotifyPropertyChanged
{
private ObservableCollection<Vehicle> _vehicles = new ObservableCollection<Vehicle>();
public ObservableCollection<Vehicle> Vehicles
{
get { return _vehicles; }
set { _vehicles = value; }
}
and also I have some properties that I get the value from this main list, for example
public int GetType1Vehicles
{
get { return Vehicles.Where(x => x.Type == Type1).Count(); }
}
public int TotalVehicles
{
get { return Vehicles.Count(); }
}
I binded a UI textbox to a "GetType1Vehicles" and another textbot to a "TotalVehicles". The problem is that when I update the Vehicle List, the ListView with all vehicles is correctly updated but the Total and the Type don't. What I doing wrong?
Anybody can help me? Thanks!
UPDATE
I found a workarround, but I'm not sure that is the best approach. Every time that I change some from the list, call manually the methods:
RaisePropertyChanged("GetType1Vehicles");
RaisePropertyChanged("TotalVehicles");
Is the correct approach?
You are correct in that you will need to manually call RaisePropertyChanged. Anyway, why do you have a TotalVehicles property when you could just bind to "Vehicles.Count"?
David is right, why this TotalVehicles property ?
With an ObservableCollection, you should not call the Count() method, just use the Count property.
The Count method is usefull if you want to count only the items with a specific value, not the size of the collection, that's the job of the property.

Resources