MAUI Bound Entry does not refresh even though the underlying value changes - refresh

I am really quite confused now, and I am sure that this is something quite simple.
On the "Expense" screen I have this simplified entry:
<Entry
x:Name="entryCountOfReceipts"
Text="{Binding Expense.CountOfReceipts, Mode=OneWay}"
IsReadOnly="true"
TextChanged="EntryCountOfReceipts_TextChanged">
</Entry>
The goal is to show that this expense has N receipts attached.
Why do I have an Entry instead of a label ? Because I want to be able to trigger in the "Expense" page a comparison between the original Expense object and the current one in order to enable or disable the save button.
This comparison is done like this:
In my xaml.cs I have this code:
private void EntryCountOfReceipts_TextChanged(object sender, TextChangedEventArgs e)
{
if (!ScreenIsStillLoading)
{
viewModel.ChangesWereMade = Utils.Utils.ChangesWereMade(viewModel.Expense, er_ExpenseOriginal);
}
}
and my Save button has these dataTriggers:
<DataTrigger TargetType="Button" Binding="{Binding ChangesWereMade}" Value="false">
<Setter Property="IsEnabled" Value="false" />
<Setter Property="BackgroundColor" Value="Gray" />
</DataTrigger>
<DataTrigger TargetType="Button" Binding="{Binding ChangesWereMade}" Value="true">
<Setter Property="IsEnabled" Value="true" />
<Setter Property="BackgroundColor" Value="Black" />
</DataTrigger>
Okay. I load the screen with an expense already having in the SQLite database two receipts attached.
My entry correctly shows 2.
In the viewModel the expense is set up correctly:
[ObservableProperty]
private Expense expense;
My model looks like this (simplified):
public class Expense
{
public double Amount { get; set; }
[PrimaryKey, AutoIncrement]
public int? ID { get; set; }
[Ignore]
public int CountOfReceipts { get; set; }
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<Receipt> Receipts { get; set; }
}
Then I take a photo of my receipt, and if everything worked properly, I execute this:
Expense.Receipts.Add(receipt);
Expense.CountOfReceipts = Expense.Receipts.Count;
(Note: I know I could have bound my view directly to Expense.Receipts.Count, but bear with me).
So in Debug I see that the Expense.CountOfReceipts goes from 2 to 3. Correct.
My viewModel.ChangesWereMade gets set to true, and my Save button gets enabled. Great. But my entry does NOT get refreshed, and persists in showing 2 instead of 3.
Why is it not refreshing to the new bound value ?
What am I missing here ?
Thanks a lot.
Alex.

You could try raise property changed after add new receipt:
Expense.Receipts.Add(receipt);
Expense.CountOfReceipts = Expense.Receipts.Count;
OnPropertyChanged(nameof(Expense));
Note: you could check the text value in EntryCountOfReceipts_TextChanged handler
private void EntryCountOfReceipts_TextChanged(object sender, TextChangedEventArgs e)
{
var a = e.OldTextValue;
var b = e.NewTextValue; // please confirm if the newtextvalue is the value you want
}
Hope it works for you.

Related

ClickOnce manual update still asks for update

I disabled update checking in Visual Studio by unchecking the publish property The application should check for updates.
My app checks for updates and the user has to option to decline to update.
The issue is when the user skips the update, the next time he starts the app the default ClickOnce update screen is presented again.
How do I make sure it never shows the default ClickOnce update dialog?
My update code:
private void CheckForUpdates()
{
if (!ApplicationDeployment.IsNetworkDeployed)
{
return;
}
var currentDeployment = ApplicationDeployment.CurrentDeployment;
UpdateCheckInfo info;
try
{
info = currentDeployment.CheckForDetailedUpdate();
}
catch (Exception e)
{
return;
}
if (!info.UpdateAvailable)
{
return;
}
var changelogDialog = new Changelog();
if (changelogDialog.ShowDialog() != true)
{
return;
}
currentDeployment.Update();
Exit();
}
This is my manifest:
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd" manifestVersion="1.0" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns="urn:schemas-microsoft-com:asm.v2" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xrml="urn:mpeg:mpeg21:2003:01-REL-R-NS" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:co.v1="urn:schemas-microsoft-com:clickonce.v1" xmlns:co.v2="urn:schemas-microsoft-com:clickonce.v2">
<assemblyIdentity name="test.ccpl.Desktop.application" version="1.0.0.89" publicKeyToken="7613da056444d824" language="en-CA" processorArchitecture="x86" xmlns="urn:schemas-microsoft-com:asm.v1" />
<description asmv2:publisher="test ccpl" co.v1:suiteName="test" asmv2:product="test ccpl" xmlns="urn:schemas-microsoft-com:asm.v1" />
<deployment install="true" mapFileExtensions="true" co.v1:createDesktopShortcut="true">
<deploymentProvider codebase="https://test-test.test.ca/Installers/test.ccpl.Desktop.application" />
</deployment>
<dependency>
<dependentAssembly dependencyType="install" codebase="Application Files\test.ccpl.Desktop_1_0_0_89\test.ccpl.Desktop.exe.manifest" size="58997">
<assemblyIdentity name="test.ccpl.Desktop.exe" version="1.0.0.89" publicKeyToken="7613da056444d824" language="en-CA" processorArchitecture="x86" type="win32" />
<hash>
<dsig:Transforms>
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<dsig:DigestValue>u36JKY4n1mmu2LZC3Ea5uRLheiM=</dsig:DigestValue>
</hash>
</dependentAssembly>
</dependency>
<compatibleFrameworks xmlns="urn:schemas-microsoft-com:clickonce.v2">
<framework targetVersion="4.7.2" profile="Full" supportedRuntime="4.0.30319" />
</compatibleFrameworks>
<publisherIdentity ...>
I think that everything you are seeing is by design.
As soon as you call currentDeployment.CheckForDetailedUpdate(), clickonce will store that update metadata in the registry. On next startup, clickonce will always look at this information to see if there is a pending deployment and if so, it will also determine if the user has skipped this newer version or not. If you want or not, this is by design.
However, if you really want to get rid of the clickonce Update dialog on startup, then there is an ugly solution available :-) Update: See below for a second workaround.
First, lets have a look at the registry and how everything works:
Navigate to this location:
HKEY_CURRENT_USER\Software\Classes\Software\Microsoft\Windows\CurrentVersion\Deployment\SideBySide\2.0\PackageMetadata\{2ec93463-b0c3-45e1-8364-327e96aea856}_{3f471841-eef2-47d6-89c0-d028f03a4ad5}\
You will see 3 sub keys for every clickonce application you have installed.
In my case the application is called WindowsApplication and the public key token is a619d47505395849. So the important key to look for starts with wind..tion_a619d47505395849_
You should see something similar to test..tion_7613da056444d824_xxxxxxxxxx on your side. Just iterate over the keys, look for the public key token and choose the shortest key.
Now comes the important part. Look at the value which name ends with !PendingDeployment. After calling the CheckForDetailedUpdate method, it should look like this:
This is why the Update Dialog is showing up.
And then just replace the value with this and you are done:
The Update Dialog will then not appear anymore. The user can manually check for an update within your application, accept or ignore it again and again.
You can test these steps manually to see if everything works as expected. Putting it in code should become something similar to this:
var changelogDialog = new Changelog();
if (changelogDialog.ShowDialog() != true)
{
RegistryKey key = Registry.CurrentUser.OpenSubKey(#"Software\Classes\Software\Microsoft\Windows\CurrentVersion\Deployment\SideBySide\2.0\PackageMetadata\{2ec93463-b0c3-45e1-8364-327e96aea856}_{3f471841-eef2-47d6-89c0-d028f03a4ad5}");
var subkeyName = key.GetSubKeyNames().Where(x => x.Contains("7613da056444d824")).OrderBy(x => x.Length).First();
var subkey = key.OpenSubKey(subkeyName, true);
subkey.SetValue("{2ad613da-6fdb-4671-af9e-18ab2e4df4d8}!PendingDeployment", new byte[] { 00, 00 }, RegistryValueKind.Binary);
return;
}
UPDATE
Another Workaround:
Instead of calling the built-in function CheckForDetailedUpdate(), you can create your own "CheckForUpdate" method. That is not be a big deal:
private CustomUpdateCheckInfo CheckForUpdate()
{
var info = new CustomUpdateCheckInfo();
var currentVersion = ApplicationDeployment.CurrentDeployment.CurrentVersion;
var manifestUri = ApplicationDeployment.CurrentDeployment.UpdateLocation;
using (XmlTextReader reader = new XmlTextReader(manifestUri.AbsoluteUri))
{
var doc = XDocument.Load(reader);
var version = doc.Descendants().FirstOrDefault(n => n.Name.LocalName == "assemblyIdentity").Attribute("version").Value;
info.NewestVersion = version;
info.IsUpdateAvailable = currentVersion.ToString() != version;
}
return info;
}
It will compare the currently deployed version with the newest version in the manifest file and returns an instance of CustomUpdateCheckInfo:
public class CustomUpdateCheckInfo
{
public bool IsUpdateAvailable { get; set; }
public string NewestVersion { get; set; }
}

Sitecore update field just before publish

I have a need to update a date time field in Sitecore just before the item is published. This will act like a "publish date time" when the item is actually published. I have successfully implemented this in the workflow and that works fine for items in the workflow by adding a custom action.
For items not in workflow and that should be picked up by the Publish agent, I tapped into the pipeline and added a processor just before the PerformAction processor. The field gets updated fine in the master database but it's never published by the publish agent to the web database. The item with all other values before the field update goes through fine.
I have tried to debug the issue and feel like it's happening because the updated item is not reflected as part of the publishqueue. Is there a way I can force the update of the date time field also published in the same process instead of having to force it to publish?
Any suggestions are welcome.
You are right updated item is not part of the publish queue. You need to put your code into publish:itemProcessing event.
You need to follow next steps:
Add a handler class into
<event name="publish:itemProcessing" help="Receives an argument of type ItemProcessingEventArgs (namespace: Sitecore.Publishing.Pipelines.PublishItem)"/>
Your publish:itemProcessing event will look like
<event name="publish:itemProcessing" help="Receives an argument of type ItemProcessingEventArgs (namespace: Sitecore.Publishing.Pipelines.PublishItem)">
<handler type="YourNameSpace.SetPublishDate, YourAssembly" method="UpdatePublishingDate"/>
</event>
Create your own class for processing items on publish:
public class SetPublishDate
{
/// <summary>
/// Gets from date.
/// </summary>
/// <value>From date.</value>
public DateTime FromDate { get; set; }
/// <summary>
/// Gets to date.
/// </summary>
/// <value>To date.</value>
public DateTime ToDate { get; set; }
public void UpdatePublishingDate(object sender, EventArgs args)
{
var arguments = args as Sitecore.Publishing.Pipelines.PublishItem.ItemProcessingEventArgs;
var db = Sitecore.Configuration.Factory.GetDatabase("master");
Item item = db.GetItem(arguments.Context.ItemId);
if (item != null)
{
using (new Sitecore.Data.Events.EventDisabler())
{
using (new EditContext(item))
{
//PublishDateFieldName must be datetime field
item["PublishDateFieldName"] = DateTime.Now;
}
}
}
}
Depending on what you're going to use this date for, perhaps a slight different approach might be better. The previous answer is valid and will probably work just fine. But updating the master database on publish, may let the publishing engine think that the master item has changed and needs re-publishing. (EventDisabler etc will prevent this, as well as trigger a re-index and so on... Things may get very tricky)
Alternatively, you can write the publish date on the item in the web database instead.
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<publishItem>
<processor type="Sitecore.Publishing.Pipelines.PublishItem.PerformAction, Sitecore.Kernel">
<patch:attribute name="type">Your.Namespace.PerformAction, Your.Assembly</patch:attribute>
</processor>
</publishItem>
</pipelines>
</sitecore>
</configuration>
And an implementation similar to this:
public class PerformAction : Sitecore.Publishing.Pipelines.PublishItem.PerformAction
{
public override void Process(PublishItemContext context)
{
base.Process(context);
if (context.Aborted || context.VersionToPublish == null || context.VersionToPublish.Source == null)
return;
var target = context.PublishOptions.TargetDatabase.GetItem(context.VersionToPublish.ID, context.VersionToPublish.Language);
if (target == null)
return;
using (new EditContext(target, false /*updateStatistics*/, true /*silent*/))
{
DateField lastPublished = target.Fields["LastPublished"]
lastPublished.Value = Sitecore.DateUtil.IsoNo;
}
}
}
John West have a blog post about this here:
http://www.sitecore.net/learn/blogs/technical-blogs/john-west-sitecore-blog/posts/2011/08/intercept-item-publishing-with-the-sitecore-aspnet-cms.aspx
Having the publish date stored in the web database, you can either read it from there instead, or create a computed index field for the master db, containing the date from the web db instead.
This can perhaps be a more robust solution, but again, it depends on what you're using the field for and if you're in control of the code reading the value.

How can I pass some information in to a Sitecore Webforms for Marketers custom message processor pipeline?

I have a site built using Sitecore 7.5 and Webforms for Marketers 2.5. I am trying to create a custom email message processor pipeline command that will change the TO field for the email before it goes out. But the proper email address needs to come from a Session variable.
Here is my class:
public class CustomEmailMessageProcessor
{
public void Process(ProcessMessageArgs args)
{
//Change the TO address for the email based on the selection in the Subject field
var subjectField = args.Fields.GetEntryByName("Subject");
if (subjectField == null)
{
return;
}
//The value of the selected item will be the ID of a Subject Option
var selectedSubjectOptionItem = Sitecore.Context.Database.GetItem(new ID(subjectField.Value));
if (selectedSubjectOptionItem == null)
{
return;
}
var selectedSubjectOption = selectedSubjectOptionItem.GlassCast<Contact_Us_Subject_Option>();
//Based on the currently selected Region in the Session variable, get the proper
//child of selectedSubjectOption to populate the TO field
???
if (args.To.Length != 0)
{
args.To.Append(",");
}
args.To.Append(proper email address goes here);
}
}
And here is my associated config:
<processMessage>
<processor type="Sitecore.Form.Core.Pipelines.ProcessMessage.ProcessMessage, Sitecore.Forms.Core" method="ExpandLinks" />
<processor type="Sitecore.Form.Core.Pipelines.ProcessMessage.ProcessMessage, Sitecore.Forms.Core" method="ExpandTokens" />
<processor type="Sitecore.Form.Core.Pipelines.ProcessMessage.ProcessMessage, Sitecore.Forms.Core" method="AddHostToItemLink" />
<processor type="Sitecore.Form.Core.Pipelines.ProcessMessage.ProcessMessage, Sitecore.Forms.Core" method="AddHostToMediaItem" />
<processor type="Sitecore.Form.Core.Pipelines.ProcessMessage.ProcessMessage, Sitecore.Forms.Core" method="AddAttachments" />
<processor type="Sitecore.Form.Core.Pipelines.ProcessMessage.ProcessMessage, Sitecore.Forms.Core" method="BuildToFromRecipient" />
<!-- Custom setting -->
<processor type="myProject.CustomEmailMessageProcessor, myProject" method="Process" />
<processor type="Sitecore.Form.Core.Pipelines.ProcessMessage.ProcessMessage, Sitecore.Forms.Core" method="SendEmail" />
</processMessage>
The user will select a Subject from a dropdown list on the form. The value of each Subject will be the Guid of a corresponding item. That subject item will have children. Each child will have an email address and will correspond to a Region item in the system. I will then find the child item that matches the Region ID that is stored in Session. Then I will have the correct email address to send the email to.
However I have no idea how to access the Session variable from within the pipeline (or if it is even possible). It doesn't have to be Session. I am happy to pass in the currently selected Region in some other fashion. I just need some way to pass information in that can be accessed from the pipeline code.
Thanks,
Corey
When using the Sitecore WFFM webservice, the save action is run on the CMS, shell site. The save action don't know anything about the user session.
A different more standard solution is create a (custom) Field hidden, and set there the value you can use to find the needed email address.
Create a custom save action and do there your e-mail sending.
Note: If you do not have a good identifier to find the email address watch out with putting an email address or to simple identifier in a (hidden) form value, it can be abused.
Example of a hidden field, based on a <input type="text" .....> for Webforms
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using Sitecore.Form.Web.UI.Controls;
namespace StockpickSitecore.Controls.WFFM.CustomFields
{
public class HiddenEmailfield : SingleLineText
{
private static readonly string baseCssClassName = "scfSingleLineTextBorder";
private string CssClassName { get; set; }
public int MaxLength
{
get
{
return this.textbox.MaxLength;
}
set
{
this.textbox.MaxLength = value;
}
}
public int MinLength { get; set; }
public new string CssClass
{
get
{
return base.CssClass;
}
set
{
base.CssClass = value;
}
}
public HiddenEmailfield()
{
this.Text = "info#yourdomein.com";
}
protected override void OnInit(EventArgs e)
{
this.Attributes.Add("style", "display: none;");
this.textbox.CssClass = "scfSingleLineTextBox";
this.help.CssClass = "scfSingleLineTextUsefulInfo";
this.generalPanel.CssClass = "scfSingleLineGeneralPanel";
this.title.CssClass = "scfSingleLineTextLabel";
this.textbox.TextMode = TextBoxMode.SingleLine;
this.Controls.AddAt(0, (Control) this.generalPanel);
this.Controls.AddAt(0, (Control) this.title);
this.generalPanel.Controls.AddAt(0, (Control) this.help);
this.generalPanel.Controls.AddAt(0, (Control) this.textbox);
}
}
}
Change the constructor HiddenEmailfield and set the this.Text with the Session value.
Creat a item in Sitecore below /sitecore/system/Modules/Web Forms for Marketers/Settings/Field Types/Custom/
based on Template: /sitecore/templates/Web Forms for Marketers/Field Type
Fill in the Assembly : StockpickSitecore for Example
Fill in the Class: StockpickSitecore.Controls.WFFM.CustomFields.HiddenEmailfield
Now you can use HiddenEmailfield in you webform als field type

Spring MVC: SessionAttributes and List

I am facing an issue with spring and it goes as follows:
In SessionAttributes I have an object person with an attribute addresses which is a list. Whenever person is updated via controler, previous entries still remain. So for example if I had in person addresses: old address 1, old address 2, old address 3 and I update person via form to have only one new address, list of addresses becomes: new address 1, old address 2, old address 3 while intended behavior is to have "new address 1" only. I couldn't seem to find a workaround this problem. I am using Spring 3.0.X.
Please find below all related code that shows the issue on hand.
Person.java:
package com.convert.dashboard.web.test;
import java.util.List;
public class Person {
private String name;
private Integer age;
private List<String> addresses;
public Person(List<String> addresses) {
this.addresses = addresses;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public List<String> getAddresses() {
return addresses;
}
public void setAddresses(List<String> addresses) {
this.addresses = addresses;
}
}
TestController.java
package com.convert.dashboard.web.test;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;
#Controller
#RequestMapping("/test")
#SessionAttributes("person")
public class TestController {
#RequestMapping(value = "/")
public ModelAndView xyz() {
ModelAndView mav = new ModelAndView();
List<String> abc = new ArrayList<String>();
abc.add("old address1");
abc.add("old address2");
abc.add("old address3");
Person person = new Person(abc);
mav.addObject("person", person);
mav.setViewName("cForm");
return mav;
}
#RequestMapping("/save")
public #ResponseBody
String process(#ModelAttribute("person") Person person) {
return "<body>" + " Name:" + person.getName() + " Age: " + person.getAge() + " Addresses: " + person.getAddresses();
}
}
cForm.jsp:
<%# taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%# taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%# taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>populate form</title>
</head>
<body>
<form:form modelAttribute="person" action="/dashboard/test/save">
<form:hidden path="name" value="X" />
<form:hidden path="age" value="20" />
<form:hidden path="addresses[0]" value="New address" />
<input type="Submit" value="Submit" />
</form:form>
</body>
</html>
There are a couple of design issues I would like to address.
You are using a single object for both form binding and domain data. This tends to cause problems exactly like the one you've encountered here. The problem is not that the form fails to "clear" out the addresses of the session object; the problem is that the session object leaks its data structure to the form, which causes binding problems.
The form has knowledge about the contents of the Person object. Specifically, the form expects there to be three addresses in the person.getAddresses() list. Like (1) above, the problem is a leak of the domain structure into the view layer.
I recommend that you create two different "person" classes: one to represent the domain data (the session object), and one to exactly mirror the structure of the form (the form binding object). Your form will contain fields that map directly to properties in a PersonForm class, and in your TestController you can take the data from the PersonForm and update the session Person appropriately. Then the form inputs don't need to be designed to handle different states of the Person.addresses list.
This approach does require a bit more code, but not terribly much, and the savings in form complexity and form/domain decoupling is well worth it.
So the solution goes as follows:
By appending an AJAX call on remove on the client side and adding the code below to the controller.
#RequestMapping("/remeveAddress")
public #ResponseBody String removeElement(#ModelAttribute("person") Person person, #RequestParam Integer addressId, Model model){
person.getAddresses().remove(addressId);
model.addAttribute("person", person);
return "{}";
}
I was facing the same issue and solved it by introducing an init binder to the controller.
The init binder resets the list of addresses for the session attribute.
Depending on requirements it could do some more sophisticated job.
#InitBinder
public void initBinder(HttpSession session) {
Person person = (Person)session.getAttribute("person");
person.getAddresses().clear();
}
Hope that could help someone else :)
Recently I've faced with the same problem, and I've found a very simple solution.
So you need just to add a hidden field for your list.
Spring conversion service will set empty list from the blank string value of this hidden field :)
You can see the solution below:
<input type='hidden' name='addresses' value='' />
And after that put your other code:
<form:input path="addresses[0]" />
<form:input path="addresses[n]" />
Notes:
If you need to convert String to List of YourClass'es, and you haven't own convertors or editors for that.
So make sure that at least ObjectToObjectConverter can do it, YourClass must have constructor with a String arg or the static method valueOf(String).
Yet another possible solution is to use a handler interceptor as follows.
servlet.xml:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/test/**" />
<bean class="com.convert.dashboard.web.test.PersonInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
PersonInterceptor.java:
public class PersonInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
Person person = (Person)request.getSession().getAttribute("person");
if (person != null)
person.getAddresses().clear();
return super.preHandle(request, response, handler);
}
}

How do I handle click event in Spark List control in Flex 4

I have a s:List component. I want to handle a click event to know what list item is selected. I don't see Click event in s:List. Any workarounds?
Thanks.
I know I'm late to the party here, but the simplest way to get the selected node from list in a click event is to use the currentTarget property.
function myClickHandler(event:MouseEvent):void{
Alert.show("My Var: " + event.currentTarget.selectedItem.myVar);
}
<s:List ... click="myClickHandler(event);">
...
</s:List>
see:
http://help.adobe.com/en_US/flex/using/WS2db454920e96a9e51e63e3d11c0bf69084-7cdb.html
You can use the IndexChangeEvent.CHANGE on List
http://docs.huihoo.com/flex/4/spark/events/IndexChangeEvent.html
Package spark.events
Class public class IndexChangeEvent
Inheritance IndexChangeEvent Event Object
Language Version: ActionScript 3.0
Product Version: Flex 4
Runtime Versions: Flash Player 10, AIR 1.5
The IndexChangeEvent class represents events that are dispatched when an index changes in a Spark component.
See also
spark.components.supportClasses.ListBase
spark.components.List
spark.components.ButtonBar
I figured how to do this. Thought I would share so that it helps others like me:
<s:List id="taskList" creationComplete="taskList.addEventListener('listClickEvent',handleListClick);" width="100%" height="80%" labelField="description" dataProvider="{todoList}"
useHandCursor="true">
<s:itemRenderer>
<fx:Component>
<s:ItemRenderer click="handleClick(event)">
<fx:Script>
<![CDATA[
import ListClickEvent;
import flash.events.MouseEvent;
import mx.controls.Alert;
private function handleClick(me:MouseEvent):void
{
var listClickEvent:ListClickEvent = new ListClickEvent("listClickEvent");
listClickEvent.index = itemIndex;
owner.dispatchEvent(listClickEvent);
}
]]>
</fx:Script>
<s:Label text="{data.description}" top="5" bottom="5" right="3" left="3"/>
</s:ItemRenderer>
</fx:Component>
</s:itemRenderer>
</s:List>
Another way:
<s:List id="myid"
dataProvider="{listDP}"
width="100%"
height="100%"/>
on application creation complete:
myid.addEventListener(MouseEvent.CLICK,clickHandler);
Handler:
private function clickHandler(event:MouseEvent):void
{
if(myid.selectedIndex>=0)
{
...
}
myid.selectedIndex=-1;//to detect click on same item
}
Thanks guys,
Just make sure your List has its id variable set. Then you call in your click handler function like this:
private function listClickHandler(event:IndexChangeEvent) {
if(myListsID.seletectedIndex == 0){
navigator.pushView(whateverViewyouwant)
} else if(myListsID.selectedIndex ==1){
navigator.pushView(changetoanotherview)
} else if(myListsID.selectedIndex == 2){
navigator.pushView(mobileViewsareEasy)
} else if(myListsID.selectedIndex == 3){
navigator.pushView(wowSomanyViews)
}
}
The variable that goes into the pushView function corresponds to the mxml file name for the view you want to load
That is farrrr too complex here is a better way:
<s:List id="whatever" dataProvider="{allMyData}" click="whateverList_click(event)"> </s:List>
<fx:Script>
var whatWasClicked:String = whatever.dataProvider.getItemAt(whatever.selectedIndex).label;
</fx:Script>
Boo ya.
<s:List id="lstDesc" width="100%" height="100%">
<s:change>
Descselected();//do your stuff here
</s:change>
</s:List>
in flash builder.