I have an activity which receives an intent with a putExtra from other activity.
And I want to create a SharedPreference each time the activity receives the putExtra value, in this case a String so I can show all the Strings stored and show in a TextView without loosing the previous String shown.
tvTextView = (TextView)findViewById(R.id.tvTextView);
Bundle extras = getIntent().getExtras();
linearLayout = (LinearLayout)findViewById(R.id.linearLayout);
if (extras != null) {
newNote = extras.getString("Note");
Button noteButton = new Button(this);
noteButton.setText(newNote);
linearLayout.addView(noteButton);
// and get whatever type user account id is
SharedPreferences prefs = getSharedPreferences("MisPreferencias",getApplication().MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putString("newNote", note);
editor.commit();
String note = prefs.getString("newNote", "Welcome");
tvDiario.setText(note);
This is my code but it only saves the last string I want to show a string and then when I get a different String from intent show it but keep showing the previous, as a story. I need to edit the SharedPreference with different values each time the activity receive the putExtra data.
I don't think you fully understand SharedPreferences; it's a key-value data storage. By calling:
editor.putString("newNote", note);
you always override your saved value under the newNote key by new value note. editor.putString does not append your new data to your stored data, it rewrites the data.
The solution: you need to get your stored data first, append new data to those stored data, and store the updated data. Try:
newNote = extras.getString("Note");
...
SharedPreferences prefs = getSharedPreferences("MisPreferencias", getApplication().MODE_PRIVATE);
String storedNotes = prefs.getString("notes", "");
SharedPreferences.Editor editor = prefs.edit();
editor.putString("notes", storedNotes + newNote + "\n");
editor.commit();
String notes = prefs.getString("notes", "");
tvDiario.setText(notes);
I exchanged your newNote key by notes to better describe what is actually stored. I also recommend to read on documentation of Editor.putString and SharedPreferences.getString, I feel like you don't have a clear idea of the interface yet.
Related
So I'm trying to make a program which shows you a word and if you click the button, it shows its definition. And first, I'm trying to make it work by adding the word/definition using getSharedPrefrences
private void savePrefrences(){
SharedPreferences pref = getSharedPreferences("pref", MODE_PRIVATE);
SharedPreferences.Editor editor = pref.edit();
editor.putString("", "");
editor.commit();
}
, but you need key name in the editor.putString("Key name Here","word/definition here")to store the thing inside the key. But I don't know how to generate random amount of key names.
HELP ME FAST!!
I'm trying to use the Acumatica API (Version 4.20.2231) for the Lead screen to convert a Lead to a Business Account. I was a bit worried about this even before I tried it because when you use the "Convert to Business Account" Action in Acumatica, it pops up a small dialog box. Here's what it looks like in Acumatica after choosing Actions > Convert to Business Account:
With the API, the first arrangement of commands I tried was failing to convert the Lead and not producing any kind of error. Eventually, I found a sequence of commands that produced an error that references dialog boxes, so that makes me think I'm on the right track. Maybe I just don't know how to manipulate the dialog box using the Commands. Does anyone know where I'm going wrong? Here is my code:
Public Function ConvertLeadToCustomer(ByVal leadID As String, ByVal firstName As String, ByVal lastName As String, ByRef companyName As String) As String
Dim CR301000 As CR301000Content = m_context.CR301000GetSchema()
m_context.CR301000Clear()
' converting a lead requires that there is a value for company, so create one if it is blank
If companyName = "" Then
companyName = lastName & ", " & firstName
End If
' create key field
Dim leadKeyValue As Value = New Value With {.LinkedCommand = CR301000.LeadSummary.LeadID, .Value = leadID}
' create company field, since its required
Dim companyValue As Value = New Value With {.LinkedCommand = CR301000.DetailsSummary.CompanyName, .Value = companyName, .Commit = True}
Dim updateLeadCommands As Command() = {leadKeyValue, CR301000.Actions.ActionConvertToBAccount, companyValue, CR301000.Actions.Save}
Dim updateLeadResult As CR301000Content() = m_context.CR301000Submit(updateLeadCommands)
' TO DO: search Business Accounts by name to find new Business Account ID
Dim newBAID As String = ""
Return newBAID
End Function
And here is the error returned when CR301000Submit is called:
System.Web.Services.Protocols.SoapException: Server was unable to process request. ---> PX.Data.PXDialogRequiredException
at PX.Data.DialogManager.a(String A_0, PXGraph A_1, String A_2, Object A_3, String A_4, String A_5, MessageButtons A_6, MessageIcon A_7, Boolean A_8, InitializePanel A_9)
at PX.Data.DialogManager.AskExt(PXView view, String key, InitializePanel initializeHandler, Boolean repaintControls)
at PX.Data.PXView.AskExt(InitializePanel initializeHandler, Boolean refreshRequired)
at PX.Objects.CR.LeadMaint.ConvertToBAccount(PXAdapter adapter)
at PX.Data.PXAction`1.a(PXAdapter A_0)
at PX.Data.PXAction`1.<Press>d__c.MoveNext()
at PX.Data.PXAction`1.<Press>d__c.MoveNext()
at PX.Data.PXAction`1.<Press>d__c.MoveNext()
at PX.Data.PXAction`1.<Press>d__c.MoveNext()
at PX.Api.SyImportProcessor.SyStep.CommitChanges(Object itemToBypass, PXFilterRow[] targetConditions)
at PX.Api.SyImportProcessor.ExportTableHelper.ExportTable()
at PX.Api.ScreenUtils.Submit(String screenId, Command[] commands, SchemaMode schemaMode, PXGraph graph)
at PX.Api.Services.ScreenService.Submit(String id, IEnumerable`1 commands, SchemaMode schemaMode)
at PX.Api.Soap.Screen.ScreenGate.Submit(Command[] commands)
--- End of inner exception stack trace ---
Your current question
Your error occurs because the action ConvertToBAccount is calling a Pop-Up Dialog and is expecting an answer:
if (AccountInfo.AskExt((graph, view) => graph.Views[view].Cache.Clear(), true) != WebDialogResult.OK) return contacts;
The way to tell Acumatica of it answer is to send the value "OK" before even calling the action. Depending on your configuration you might also want populate the field in this pop-up:
Dim commandsConvert As Command() =
{
New Value With {.Value = leadID, .LinkedCommand = CR301000.LeadSummary.LeadID, .Commit = True},
New Value With {.Value = "OK", .LinkedCommand = CR301000.NewAccountServicesSettings.ServiceCommands.DialogAnswer, .Commit = True}, 'This is how close the pop-up. We fill the field from the pop-up after this line
New Value With {.Value = newCompanyCD, .LinkedCommand = CR301000.NewAccountServicesSettings.BAccountID}, 'With autonumbering On, no need for this line.
New Value With {.Value = newCompanyName, .LinkedCommand = CR301000.NewAccountServicesSettings.AccountName}, 'The default value will be taken from DetailsSummary.CompanyName
CR301000.Actions.ActionConvertToBAccount
}
m_context.CR301000Submit(commandsConvert)
Your future question
Converting from a Lead to a BAccount is a 2-step process where you are redirected to your newly created BAccount and where you need to save it. It will not be converted as long as you don't save it.
This is generally a pretty straight forward process or you simply submit Save to the page where you were redirected (CR303000):
'Once the Process is completed, We want to save the new record.
'If we want to edit some information on the new Business Account
'this is the right place to do it.
Dim newBAID As String = String.Empty
Dim commandsBAccount As Command() =
{
CR303000.Actions.Save,
CR303000.AccountSummary.BusinessAccount
}
Dim newBAccountContent As CR303000Content() = m_context.CR303000Submit(commandsBAccount)
If newBAccountContent.Length > 0 Then
newBAID = newBAccountContent(0).AccountSummary.BusinessAccount.Value
End If
As long as you keep the same cookie container, the UserState should be aware that you are currently on a dirty CR303000 with cached information. If you use a custom web service endpoint that included both CR301000 and CR303000 there is nothing to deal with.
Unfortunatly, in this scenario it doesn't work.
It seems that the PXRedirectRequiredException is raised from within a PXLongOperation (read Thread) and that the Web-Service doesn't pick the dirty state of BAccount. The only solution I can find right now is to customize the action ConvertToBAccount to remove threading:
public class LeadMaintExt : PXGraphExtension<LeadMaint>
{
[PXUIField(DisplayName = Messages.ConvertToBAccount, MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Select)]
[PXButton(ImageKey = PX.Web.UI.Sprite.Main.Process)]
public virtual IEnumerable ConvertToBAccount(PXAdapter adapter)
{
List<Contact> contacts = new List<Contact>(adapter.Get().Cast<Contact>());
foreach (Contact lead in contacts)
{
if (Base.AccountInfo.AskExt((graph, view) => graph.Views[view].Cache.Clear(), true) != WebDialogResult.OK) return contacts;
bool empty_required = !Base.AccountInfo.VerifyRequired();
BAccount existing = PXSelect<BAccount, Where<BAccount.acctCD, Equal<Required<BAccount.acctCD>>>>.SelectSingleBound(Base, null, Base.AccountInfo.Current.BAccountID);
if (existing != null)
{
Base.AccountInfo.Cache.RaiseExceptionHandling<LeadMaint.AccountsFilter.bAccountID>(Base.AccountInfo.Current, Base.AccountInfo.Current.BAccountID, new PXSetPropertyException(Messages.BAccountAlreadyExists, Base.AccountInfo.Current.BAccountID));
return contacts;
}
if (empty_required) return contacts;
Base.Save.Press();
//PXLongOperation.StartOperation(this, () => ConvertToAccount(lead, AccountInfo.Current));
LeadMaint.ConvertToAccount(lead, Base.AccountInfo.Current);
}
return contacts;
}
}
I am looking for a better way to address the situation and I'll edit my answer when I find it.
The commands specified should be for the dialog not the summary.
The required fields and the dialog answer should be provided before calling the action.
Below is the sample used for create Receipt action in receipts screen.
Note: C# code
commands = new Command[]
{
new Value
{
Value = CreatedOn.HasValue ? ((DateTime)CreatedOn.Value).ToLongDateString() : "",
LinkedCommand = SOSchema.SpecifyShipmentParameters.ShipmentDate,
Commit = true
},
new Value
{
Value = "OK",
LinkedCommand = SOSchema.SpecifyShipmentParameters.ServiceCommands.DialogAnswer,
Commit = true
},
SOSchema.Actions.ActionCreateReceipt
};
context.SO301000Submit(commands);
Hope this helps.
I am new to the API and I'm trying to get values from the active view. I am using the following code as a mock up to what I'm trying to do:
public void GetViewProperties()
{
String viewname;
String typename;
String levelname;
String Output;
ViewFamilyType VfamType;
Level lev;
//Get document and current view
Document doc = this.ActiveUIDocument.Document;
View currentView = this.ActiveUIDocument.ActiveView;
//Find the view family type that matches the active view
VfamType = new FilteredElementCollector(doc).OfClass(typeof(ViewFamilyType))
.Where(q => q.Name == "1-0-Model").First() as ViewFamilyType;
//Find the level that matches the active view
lev = new FilteredElementCollector(doc).OfClass(typeof(Level))
.Where(q => q.Name == "00").First() as Level;
//Get the view's current name
viewname = currentView.Name.ToString();
//Get the name of the view family type
typename = VfamType.Name;
//Get the name of the level
levelname = lev.Name.ToString();
//Combine results for task dialog
Output = "View: " + viewname + "\n" + typename + "-" + levelname;
//Show results
TaskDialog.Show("View Properties Test",Output);
}
I'm cheating at the moment by grabbing the view type and level by name. I really want them to be found by looking at the properties of the active view. I can't figure out how I am meant to access the view type and level name properties. I need to make lambda use a variable e.g. (q => q.Name == Level.name), (q => q.Name == ViewFamilyType.name).
Thanks in advance!
Here is your code corrected:
public void GetViewProperties()
{
//Get document and current view
Document doc = this.ActiveUIDocument.Document;
View currentView = this.ActiveUIDocument.ActiveView;
//Find the view family type that matches the active view
var VfamType = (ViewFamilyType)doc.GetElement(currentView.GetTypeId());
//Find the level that matches the active view
Level lev = currentView.GenLevel;
//Get the view's current name
string viewname = currentView.Name;
//Get the name of the view family type
string typename = VfamType.Name;
//Get the name of the level
string levelname = lev.Name;
//Combine results for task dialog
string Output = "View: " + viewname + "\n" + typename + "-" + levelname;
//Show results
TaskDialog.Show("View Properties Test", Output);
}
You don't need to use a FilteredElementCollector to get these informations. And if you need elsewhere, you don't need a Where: just put your lambda in the First:
new FilteredElementCollector(doc).OfClass(typeof(ViewFamilyType))
.First(q => q.Name == "1-0-Model")
If you need to access in your lambda a property specific to a class, not defined on Element, you can use Cast:
new FilteredElementCollector(doc).OfClass(typeof(ViewFamilyType))
.Cast<ViewFamilyType>().First(vft => vft.IsValidDefaultTemplate)
And please do not declare all your variable at the start of your methods. You're not writing Pascal. Declare variables as close to the first spot that you use them as possible. It makes your code much more readable. The closer a variable is declared to where it is used, the less scrolling/searching you have to do when reading the code later and it also naturally narrow their scope.
You're probably looking for View.GenLevel property. This will work for views related to levels, such as Plan Views. Note that if this View is not generated by a level, this property is null.
I have a plugin where i am creating a new case and I want to send an email out as it is created including its ticketnumber. I have attempted just to call this in the plugin but it is coming back saying that it is not present in the dictionary. I know this field is populated using CRM's own autonumbering so what i'm guessing is happening is that my plugin is firing and creating the case but then i'm trying to use this field before the autonumber has completed.
So is there a way that i can get my plugin to "wait" until this field is available and then use it?
Thanks
EDIT: Code below:
string emailBody = entity.Attributes["description"].ToString();
int bodyLength = emailBody.Length;
int textStart = emailBody.IndexOf(">") + 1;
int newLength = bodyLength - (textStart + 7);
string description = emailBody.Substring(textStart, newLength);
//create complaint
Entity complaint = new Entity("incident");
complaint["description"] = description;
complaint["ts_case_type"] = 717750001;
complaint["ts_registration_datetime"] = DateTime.Now;
complaint["ownerid"] = Owner;
complaint["customerid"] = Organisation;
Service.Create(complaint);
As a side I would suggest sending the email with a workflow if possible, it will be far easier to maintain in the long run and quicker to implement in the short.
In any case to answer your question, from what you have here you need to update your code to retrieve the ticketnumber once you have created the incident. You can do this with a Retrieve message.
For example:
//Create the complaint
Entity complaint = new Entity("incident");
//This is the information that is being sent to the server,
//it will not be updated by CRM with any additional information post creation
complaint["description"] = description;
complaint["ts_case_type"] = 717750001;
complaint["ts_registration_datetime"] = DateTime.Now;
complaint["ownerid"] = Owner;
complaint["customerid"] = Organisation;
//Capture the id of the complaint, we will need this in a moment
Guid complaintId = Service.Create(complaint);
//complaint["ticketnumber"] <-- The server does not populate this information in your object
//Retrieve the ticketnumber from the incident we just created
Entity complaintRetrieved = service.Retrieve("incident", complaintId, new ColumnSet("ticketnumber"));
//Get the ticketnumber
String ticketNumber = (String)complaintRetrieved.Attributes["ticketnumber"];
Like James said in comment, if you just want to send email with some case properties, it is best to do that with workflow (on case create).
In your plugin, ID is generated, and you can get it with:
entity.Attributes["ticketnumber"]
Problem
As we know, SharePoint saves data in database in plain text. Some fields even have concatenated strings like <id>;#<value> for user fields. Percents are saved as doubles (1.00000000000000 for 100%) and etc.
Ofcourse, I want to display data as they are displayed in lists.
What should I do?
Should I use derived SPBoundField to format values (Which I actually did and it works fine until you want to filter (probably SPBoundField won't format me values because i use ObjectDataSource not list and with reflector I saw if there are SPListItems in datasource, then it formats correctly. Not my case)
alt text http://img199.imageshack.us/img199/2797/ss20090820110331.png
Or must I loop through all the DataTable and format each row accordingly?
What are Your techniques?
Thank you.
Here is how I solved this issue.
<asp:TemplateField HeaderText="Campaign Members">
<ItemTemplate>
<%# RemoveCharacters(Eval("CampaignMembers").ToString())%>
</ItemTemplate>
</asp:TemplateField>
// Make sure declare using System.Text.RegularExpression;
protected string RemoveCharacters(object String)
{
string s1 = String.ToString();
string newString = Regex.Replace(s1, #"#[\d-];", string.Empty);
newString = Regex.Replace(newString, "#", " ");
return newString.ToString();
}
I normaly use ItemTemplates that inherit from ITemplate. With in the ItemTemplate I use the SPFieldxxxValue classes or some custom formating code. This saves looping through the DataTable and the ItemTemplates can be reused.
The ItemTemplates are attached in Column Binding
E.G
// Normal Data Binding
SPBoundField fld = new SPBoundField();
fld.HeaderText = field.DisplayName;
fld.DataField = field.InternalName;
fld.SortExpression = field.InternalName;
grid.Columns.Add(fld);
// ItemTemplate Binding
TemplateField fld = new TemplateField();
fld.HeaderText = field.DisplayName;
fld.ItemTemplate = new CustomItemTemplateClass(field.InternalName);
fld.SortExpression = field.InternalName;
grid.Columns.Add(fld);
An example of a ItemTemplate
public class CustomItemTemplateClass : ITemplate
{
private string FieldName
{ get; set; }
public CustomItemTemplateClass(string fieldName, string formatString)
{
FieldName = fieldName;
}
#region ITemplate Members
public void InstantiateIn(Control container)
{
Literal lit = new Literal();
lit.DataBinding += new EventHandler(lit_DataBinding);
container.Controls.Add(lit);
}
#endregion
void lit_DataBinding(object sender, EventArgs e)
{
Literal lit = (Literal)sender;
SPGridViewRow container = (SPGridViewRow)lit.NamingContainer;
string fieldValue = ((DataRowView)container.DataItem)[FieldName].ToString();
//Prosses Filed value here
SPFieldLookupValue lookupValue = new SPFieldLookupValue(fieldValue);
//Display new value
lit.Text = lookupValue.LookupValue;
}
}
Here are a few options. I don't know the output of all of them (would be a good blog post) but one of them should do what you want:
SPListItem.GetFormattedValue()
SPField.GetFieldValue()
SPField.GetFieldValueAsHtml()
SPField.GetFieldValueAsText()
It may also be handy to know that if you ever want to make use of the raw values then have a look at the SPField*XYZ*Value classes. For example the form <id>;#<value> you mention is represented by the class SPFieldUserValue. You can pass the raw text to its constructor and extract the ID, value, and most usefully User very easily.
I would suggest either to format the values before binding them to the spgridview. Linq and an anonymous type is preffered or to call a code behind function on the field that needs the formatting upon binding.
DataField='<%# FormatUserField(Eval("UserFieldName")) %>'
or...maybe a templated field?
After all, i did have not know any other solution to loop through DataTable rows and format them accordingly.
If your SPGridView's data source is list, try out SPBoundField.