I've got the following problem:
I created a WebPart with a ToolPart,
this toolpart has multiple controls (textbox, dropdownlist, ...)
when I fill in everything and apply, it all goes ok,
even when i press ok. But when i go back to
edit -> modify webpart, all my data i've entered is gone.
How can i solve this?
Thanks
You'll need to save the values from the Toolpart in the webpart's properties. For example, lets say I want to save a string for "Title"... in the webpart define a property:
private const string DEFAULT_WPPColumnTitle = "Title";
private string _WPPColumnTitle = DEFAULT_WPPColumnTitle;
[Browsable(false)]
[WebPartStorage(Storage.Shared)]
public string WPPColumnTitle
{
get { return this._WPPColumnTitle; }
set { this._WPPColumnTitle = value; }
}
I always use the prefix "WPP" to keep all the web part properties together.
Then, in the Toolpart's ApplyChanges override, save the control's value (_ddlColumnsTitle) to the webpart (WPPColumnTitle):
/// <summary>
/// Called by the tool pane to apply property changes to
/// the selected Web Part.
/// </summary>
public override void ApplyChanges()
{
// get our webpart and set it's properties
MyCustomWebPart et = (MyCustomWebPart)ParentToolPane.SelectedWebPart;
et.WPPColumnTitle = _ddlColumnsTitle.SelectedValue;
}
Lastly, if the user edited the properties already, we want the Toolpart to be pre-populated with the user's configuration. In the CreateChildControls() method of your Toolpart, initialize the controls:
protected override void CreateChildControls()
{
try
{
MyCustomWebPart et = (MyCustomWebPart)ParentToolPane.SelectedWebPart;
// ... code to create _ddlColumnsTitle and add it to the Controls
// default our dropdown to the user's selection
ListItem currentItem = _ddlColumnsTitle.Items.FindByValue(et.WPPColumnTitle);
if (null != currentItem)
{
_ddlColumnsTitle.SelectedValue = currentItem.Value;
}
}
catch (Exception ex)
{
_errorMessage = "Error adding edit controls. " + ex.ToString();
}
}
Open up the debugger and double check that the values are getting applied to your propertries on Apply (i.e. WPPColumnTitle is set).
If so then problem is that SharePoint is not serializing/deserializing the value from the property (WPPColumnTitle) to the database and back - verify by writing out this property on the web part - as soon as you leave the page and come back it will be empty.
If so then check things like this on class
[XmlRoot(Namespace = "YourNamespace")]
and this (not strictly necessary) on properties
[XmlElement(ElementName = "ColumnTitle")]
I've also seen problems if you name your web part class "WebPart" so call it MyWebPart
I've solved it with adding a property in my webpart "IsNeverSet" (bool)
and when i go to the "CreateControls()" of my toolpart, I get this property
and if it's false, I load all the properties from my webpart and fill them in the toolpart.
So I found it with the help of Kit Menke
Related
I have a completely custom screen with its own BLC and DACs, and I want to open it as a popup from a button placed on the Bills and Adjustments screen. I have coded it as follows:
public class APInvoiceEntryExt : PXGraphExtension<APInvoiceEntry>
{
public PXAction<APInvoice> LaunchOpenSource;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "Open Source")]
protected void launchOpenSource()
{
APInvoice apinvoice = (APInvoice)Base.Document.Current;
if (apinvoice != null)
{
//var url = "http://localhost/AcumaticaDB2562/?ScreenId=AC302000&OpenSourceName=Bills+and+Adjustments&DataID=" + apinvoice.RefNbr;
OpenSourceDataMaint graph = PXGraph.CreateInstance<OpenSourceDataMaint>();
graph.OpenSourceDataHeader.Current = graph.OpenSourceDataHeader.Search<xTACOpenSourceHeader.openSourceName, xTACOpenSourceHeader.dataID>("Bills and Adjustments", apinvoice.RefNbr);
if (graph.OpenSourceDataHeader.Current != null)
{
throw new PXRedirectRequiredException(graph, "Open Source")
{
Mode = PXBaseRedirectException.WindowMode.NewWindow
};
}
}
}
}
I've included all the relevant DACs and BLC for my custom screen in the Class Library project I'm using to customize the 'Bills and Adjustments' screen where I'm adding the button.
The problem I'm having is that I get the following error message when launching the button:
I've set all the relevant permissions for the screen that uses the OpenSourceDataMaint BLC to 'Delete' in 'Access Right By Role', 'Access Rights By User', and 'Access Rights By Screen'. Nothing makes any difference.
Looks like DataSource is trying to find a node in SiteMap with GraphType equal to full name off your OpenSourceDataMaint class and fails:
public class PXBaseDataSource : DataSourceControl, IAttributeAccessor, INamingContainer, ICompositeControlDesignerAccessor, ICommandSource, IPXCallbackHandler, IPXScriptControl, IPXCallbackUpdatable, IPostBackDataHandler
{
...
private static string getFormUrl(Type graphType)
{
PXSiteMapNode node = getSiteMapNode(graphType);
if (node == null)
{
throw new PXException(string.Format(ErrorMessages.GetLocal(ErrorMessages.NotEnoughRightsToAccessObject), graphType.Name));
}
String url = node.Url;
//if (url.Contains("unum=")) url = PXUrl.IgnoreQueryParameter(url, "unum");
return PXUrl.TrimUrl(url);
}
...
}
Could you please check if TypeName is properly defined for PXDataSource inside your custom Aspx page? Also could you please check if your custom Aspx page also exists in Cst_Published folder and if values set for PXDataSource.TypeName property are identical inside Pages and Cst_Published folders?
One more thing to check, does the Site Map screen show the right GraphName for your custom screen? - would be beneficial if you can provide a screenshot for verification.
If possible, please provide your customization package, that can be published locally (even with compiled assembly) - this would greatly speed up the investigation process.
The solution, for me, was to put the code (shown below) in a customization window instead of a class library project in Visual Studio. Since the code needs to have a reference to another published customization, putting it inside an Acumatica code window takes care of this. There is no reference to the published custom screen customization in my class library project, and this obviously causes issues - and I'm not sure how to handle that.
public class APInvoiceEntryExt:PXGraphExtension<APInvoiceEntry>
{
public PXAction<APInvoice> LaunchOpenSource;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "Open Source")]
protected void launchOpenSource()
{
APInvoice apinvoice = (APInvoice)Base.Document.Current;
if (apinvoice != null)
{
AssistantController.OpenSourceDataMaint graph = PXGraph.CreateInstance<AssistantController.OpenSourceDataMaint>();
graph.OpenSourceDataHeader.Current = graph.OpenSourceDataHeader.Search<AssistantController.xTACOpenSourceHeader.openSourceName
,AssistantController.xTACOpenSourceHeader.dataID>("Bills and Adjustments", apinvoice.RefNbr);
throw new PXRedirectRequiredException(graph, "Open Source")
{
Mode = PXBaseRedirectException.WindowMode.NewWindow
};
}
}
}
I have a UICollectionView with images. The user can select (multiselect) the images. When the user taps a single image, everything works fine. The SelectedBackgroundView is visible and on tap again, the normal image is visible.
But my problem is, I have a option for the user "Select all". In that i want to select all items programmatically. With following code:
for (int i = 0; i < CollectionView.NumberOfItemsInSection(0); i++)
{
var ip = NSIndexPath.FromItemSection(i, 0);
CollectionView.SelectItem(ip, false, UICollectionViewScrollPosition.None);
}
The following method returns the correct number for the selected items:
var number = CollectionView.GetIndexPathsForSelectedItems().Length;
But the UI is not changing to the SelectedBackgroundView.
Can anyone help me? Thanks.
Calling SelectItem does not cause the display to be updated; it just changes the Selected property of the UICollectionViewCell therefore updating the selected index set in the collection view.
What I do is override the Selected property of my UICollectionViewCell implementation and adjust the UI at that point:
public class MyCell : UICollectionViewCell
{
// ...
public override bool Selected
{
get { return base.Selected; }
set
{
base.Selected = value;
// change the state of the selected background
imageBackground.Image = LoadAnImage(value ? "BackgroundOn" : "BackgroundOff");
}
}
}
This way ensures that the UI is updated at all possible points when the selected state of the cell changes, either by user interaction or programmatically calling SelectItem or DeselectItem on the collection view.
I do not personally use the SelectedBackgroundView property on a cell (I do my own layering, most of the time), but you may have to manually bring that view to the front yourself in a similar Selected property override.
I want to disable Orchard Content Part buttons (Save and Publish Now) in the EDITOR template (when Content Item is created) based on some conditions. Can I do that ? How do I access the buttons in the EDITOR view.
Here are come examples,
To build a content fully from a Controller example, taken from the Blog Module
public ActionResult Create() {
if (!Services.Authorizer.Authorize(Permissions.ManageBlogs, T("Not allowed to create blogs")))
return new HttpUnauthorizedResult();
BlogPart blog = Services.ContentManager.New<BlogPart>("Blog");
if (blog == null)
return HttpNotFound();
dynamic model = Services.ContentManager.BuildEditor(blog);
// Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
return View((object)model);
}
[HttpPost, ActionName("Create")]
public ActionResult CreatePOST() {
if (!Services.Authorizer.Authorize(Permissions.ManageBlogs, T("Couldn't create blog")))
return new HttpUnauthorizedResult();
var blog = Services.ContentManager.New<BlogPart>("Blog");
_contentManager.Create(blog, VersionOptions.Draft);
dynamic model = _contentManager.UpdateEditor(blog, this);
if (!ModelState.IsValid) {
_transactionManager.Cancel();
// Casting to avoid invalid (under medium trust) reflection over the protected View method and force a static invocation.
return View((object)model);
}
_contentManager.Publish(blog.ContentItem);
return Redirect(Url.BlogForAdmin(blog));
}
BuidEditor does the work for you.
And you should use a alternative version of this template, but remove the edit link and publish link.
Note, you need a route for you custom create action, and a menu link on the dashboard may come in handy.
I have inherited the coreresultswebpart in a custom webpart that I am building in sharepoint 2010.
The reason for this is because there are some properties that I want to set by default, without any manual entry.
The webpart is working fine except, when the webpart is loaded in the page, it does not immediately show the results (search result exists for the default configurations). But when I hit the enter key on the adress bar, the results are loaded. The results are also loaded when I am in edit mode. However, when I click OK in the editorpart, the results vanish and the webpart tells me to refresh page, at which point, the same cycle repeats.
What am I missing?
Here is a code snippet of where I am making the change:
protected override void OnLoad(EventArgs e)
{
CssRegistration.Register("/_layouts/WPLatestBlogFeed/LatestBlogFeed_CustomStyle.css");
base.OnLoad(e);
if (firstLoad)
{
firstLoad = false;
CustomizeWebPart();
}
}
try to override ConfigureDataSourceProperties Method.
protected override void ConfigureDataSourceProperties()
{
// run the base code
base.ConfigureDataSourceProperties();
CssRegistration.Register("/_layouts/WPLatestBlogFeed/LatestBlogFeed_CustomStyle.css");
base.OnLoad(e);
if (firstLoad)
{
firstLoad = false;
CustomizeWebPart();
}
}
So, this is what we want to do: We want to have a generic web part with a custom frame around it and then dynamically load other web parts (frameless) inside it. Would this at all be possible you think? A bit like Jan Tielens SmartPart, only not for ASP.Net User Controls, but for other Web parts... ;)
Edit: We've been able to do this now. The solution was actually pretty simple. Check out the code:
public class WebPartWrapper : System.Web.UI.WebControls.WebParts.WebPart {
protected override void CreateChildControls() {
Panel pnl = new Panel();
this.Controls.Add(pnl);
WebPart dynamicPart = WebPartFactory.CreateWebPart("RSSViewer");
pnl.Controls.Add(dynamicPart);
}
}
Easy as that... We also use reflection to store the webparts as Xml etc., but that's beside the point.
I don't think so. I tried this a while back and it complained about only being able to add WebPartZone items in Page Init. I think by the time it get's to initialising your "container" WebPart it's too late to add more zones as the holding page has already been initialised.
public class WebPartWrapper : System.Web.UI.WebControls.WebParts.WebPart {
protected override void CreateChildControls() {
Panel pnl = new Panel();
this.Controls.Add(pnl);
var factory = new WebPartFactory()
WebPart dynamicPart = factory.CreateWebPart("RSSViewer", this.Guid);
pnl.Controls.Add(dynamicPart);
}
}
public class WebPartFactory {
public WebPart CreateWebpart(string webpartName, Guid parentWebPartGuid)
{
var config = ConfigurationFactory.LoadConfiguration(webpartName);
Assembly webPartAssembly = Assembly.Load(config.Assembly);
Type webPartType = webPartAssembly.GetType(config.Class);
object actualWebPart = Activator.CreateInstance(webPartType);
foreach (var item in config.Properties)
{
PropertyInfo webPartProperty = webPartType.GetProperty(item.Name);
object webPartPropertyValue = Convert.ChangeType(itemValue, Type.GetType(item.Type));
if (!String.IsNullOrEmpty(item.Value))
webPartProperty.SetValue(actualWebPart, webPartPropertyValue, null);
}
RunMethod("set_StorageKeyInternal", actualWebPart, new object[] { parentWebPartGuid });
return actualWebPart as WebPart;
}
private void RunMethod(string methodName, object objectInstance, object[] methodParameters)
{
BindingFlags flags = BindingFlags.Instance | BindingFlags.Public |
BindingFlags.NonPublic;
Type t = objectInstance.GetType();
MethodInfo m = GetMethod(t, methodName, flags);
if (m != null)
{
m.Invoke(objectInstance, methodParameters);
}
}
private MethodInfo GetMethod(Type instanceType, string methodName, BindingFlags flags)
{
MethodInfo m = instanceType.GetMethod(methodName, flags);
if (m != null)
{
return m;
}
if (instanceType.GetType() == typeof(object) || instanceType.BaseType == null)
{
return null;
}
return GetMethod(instanceType.BaseType, methodName, flags);
}
}
This code needs some explaining... Please excuse me if it does not compile, I had to remove a fair bit of the original code, it was very implementation specific stuff. I've not shown the "config" class either, it's just a container for configuration of webparts, just a bunch of properties. There are 2 issues I'd like to discuss in more detail:
parentWebPartGuid - This is the Guid (UniqueId?) of the hosting webpart. For some reason we have to set "StorageKeyInternal" to this value, using reflection (it's a private property). You can possibly get away with not setting it, but at least for the majority of webparts we had to set it.
config.Properties - This is the config values (we set them in a custom .xml file, but feel free to get this from anywhere). It can look a little like this..
In our framework we also support stuff like dynamic property values etc., but that's for another day... Hope this all makes sense and can help somebody.
There are (at least) two ways to do this: using iframe HTML element, or just a div whose content is changed by JavaScript (probably with Ajax).
[NOTE] My answer is generic (ie. on Web design side), I have no idea how it in your technical context, so maybe I should delete this answer...
No chance on getting the source for the WebPartFactory class is there? Or maybe a bit more information about it? Pseudo code maybe? If a custom web part is in the gallery it could be referenced in the same way as RSSViewer is correct? I'm just not really sure how to go about doing what you have done here, and I would very much like to better understand how to do this.
Thanks!
When a want to instantiate a custom webpart inside another custom webpart i use the following code in the .ascx
<%# Register tagPrefix="uc1" Namespace="Megawork.Votorantim.Intranet.Webparts_Intranet.LikeButton" Assembly="Megawork.Votorantim.Intranet, Version=1.0.0.0, Culture=neutral, PublicKeyToken=769156d154035602" %>
The Namespace value and the Assembly value can be copied from the SafeControls line from the webconfig or from the package file (in manifest tab) :)
When i want to instantiate it dinammicaly (in fact) is use the following code in the .cs
//This is the namespace of the control that will be instantiated dinamically
string type = "My.Custom.Namespace.WebpartToBeAdded.WebpartToBeAdded";
// Instantiate the control dinamically based on his type
System.Web.UI.WebControls.WebParts.WebPart genericWP = (System.Web.UI.WebControls.WebParts.WebPart)Activator.CreateInstance(Type.GetType(type));
// sets the page to the genericWP (i dont know if this is required)
genericWP.Page = this.Page;
// Note: if you want to call custom methods of the dinamically instantiated controls (like a custom load method) you will need to create an interface and make your dinamically instantiated webpart implement it. You will need to do it in that file that have the following code: private const string _ascxPath #"~/_CONTROLTEMPLATES/...". Then you can do the following
//IMyInterface ig = (IMyInterface)genericWP;
//ig.MyCustomLoadMethod(someParam);
// Adds the controls to a container, an asp panel by example.
panelDinamicControls.Controls.Add(genericWP);