I want to use the MultipleLookupField control in a web page that will run in the context of SharePoint. I was wondering if anyone would help me with an example, which shows step by step how to use the control two display two SPField Collections.
I'm not entirely sure I understand your question, especially the bit about displaying two SPField collections. Sorry if this turns out to be the answer to a completely different question!
Anyway here's a quick demo walkthrough of using the MultipleLookupField in a web part.
Create a team site. Add a few tasks to the task list. Also put a document in the Shared Documents library. Create a new column in the Shared Documents library; call it "Related", have it be a Lookup into the Title field of the Tasks list, and allow multiple values.
Now create a web part, do all the usual boilerplate and then add this:
Label l;
MultipleLookupField mlf;
protected override void CreateChildControls()
{
base.CreateChildControls();
SPList list = SPContext.Current.Web.Lists["Shared Documents"];
if (list != null && list.Items.Count > 0)
{
LiteralControl lit = new LiteralControl("Associate tasks to " +
list.Items[0].Name);
this.Controls.Add(lit);
mlf = new MultipleLookupField();
mlf.ControlMode = SPControlMode.Edit;
mlf.FieldName = "Related";
mlf.ItemId = list.Items[0].ID;
mlf.ListId = list.ID;
mlf.ID = "Related";
this.Controls.Add(mlf);
Button b = new Button();
b.Text = "Change";
b.Click += new EventHandler(bClick);
this.Controls.Add(b);
l = new Label();
this.Controls.Add(l);
}
}
void bClick(object sender, EventArgs e)
{
l.Text = "";
foreach (SPFieldLookupValue val in (SPFieldLookupValueCollection)mlf.Value)
{
l.Text += val.LookupValue.ToString() + " ";
}
SPListItem listitem = mlf.List.Items[0];
listitem["Related"] = mlf.Value;
listitem.Update();
mlf.Value = listitem["Related"];
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
EnsureChildControls();
}
Granted, this is borderline ridiculous -- everything is hard-coded, there is no error-handling at all, and it serves no useful purpose -- but it's only meant as a quick demo. Now build and deploy this web part and add an instance of it to your team site's homepage; it should allow you to get and set the tasks which are associated with the first document in the library.
The strange bit towards the end of the button Click handler, where we read a value from mlf.Value and then write it back again, appears to be required if you want the UI to stay in sync with the actual list values. Try omitting the last line of bClick to see what I mean. This has been driving me nuts for the last hour or so, and I'm hoping another commenter can come up with a better approach...
Hm. Works fine on mine, so let's see if we can work out how your setup is different...
It looks as though it's having trouble populating the control; my first guess would be that this is because the code makes so many assumptions about the lists it's talking to. Can you check that you've got a plain vanilla Team site, with (assume these names are case-sensitive):
A list called Tasks, with several items in it
A library called Shared Documents with at least one document
A column called Related in the Shared Documents library
The Related column is a Lookup field into the Title column of Tasks, and allows multiple values.
The first document in Shared Documents has a value for Related
Then add the webpart. Fingers crossed...
Hm. OK, I'm still trying to break mine... so I went to the layouts directory and created a file foo.aspx. Here it is:
<%# Page Language="C#" Inherits="System.Web.UI.Page" MasterPageFile="~/_layouts/simple.master" %>
<%# Register Tagprefix="foo" Namespace="Foople" Assembly="Foople, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9f4da00116c38ec5"%>
<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">
<foo:WebPart1 id="fred" runat="server" />
<foo:WebPart1a id="barney" runat="server" />
</asp:Content>
WebPart1 is the webpart from before. WebPart1a is the exact same code, but in a class that inherits directly from WebControl rather than from WebPart.
It works fine, apart from a security validation problem on the postback that I can't be bothered to debug.
Changing the masterpage to ~masterurl/default.master, I uploaded foo.aspx to the Shared Documents library, and it works fine from there too -- both the WebControl and the WebPart behave properly, and the security problem is gone too.
So I'm at a loss. Although I did notice this page with an obscure might-be-bug which is also in SPFolder.get_ContentTypeOrder(): http://forums.msdn.microsoft.com/en-US/sharepointdevelopment/thread/63baf273-7f36-453e-8293-26417759e2e1/
Any chance you could post your code?
Related
I have a problem with a site definition, it has several site pages, in those pages i need to have a top menu, so i created a user control, that draw that menu, and added it to the pages.
After deploying the solution i can create the site just fine.
In the initial phase, i have hard coded values for the menu links, and in each of the pages i could see the menu.
Now the menu it's dynamic, so i have a sharepoint list with the menu options, so i changed the code, instead of hard-coded values, i get them from a list, the code runs fine, i can check that while debugging, but in the end i get a complete blank page.
I can't interact with sharepoint data inside a user control that will be used in a site page?
I tested the control in a normal/application page and it works just fine, in both cases (hard-coded and sharepoint data).
A simple sample of the code:
protected void Page_Load(object sender, EventArgs e)
{
lblDate.Text = DateTime.Now.ToLongTimeString();
ddlData.Items.Clear();
//Option Manual - WORKS FINE
ddlData.Items.Add("Manual 1");
ddlData.Items.Add("Manual 2");
ddlData.Items.Add("Manual 3");
//Option Sharepoint Data - DOESNT WORK
SPSecurity.RunWithElevatedPrivileges(delegate () {
using (SPSite site = SPContext.Current.Site)
{
using (SPWeb web = site.RootWeb)
{
foreach (SPList lista in web.Lists)
{
ddlData.Items.Add(lista.Title);
}
}
}
});
}
The rest of the code, it's a simple page with the reference to this user control, and the aspx code of the user control only have the declaration of this two controls.
After several tests, i came to conclusion that i have two problems:
1 - In this project for some reason, unknown to me, inside the code someone had added some code to clear the errors, that's why i had the blank page.
2 - After seeing the error, i could confirm that if i use site pages and trying to get the data with Using, when it tries to dispose the objects it throws the exception, so the solution was simple, just remove the Usings.
Thanks
We have a solution that deploys a number of lists and pages. We wan't to create links for them on the Quick Launch menu automatically when a feature is activated.
The structure could be something like this.
Customers
Active
Inactive
Sales
Quotes
Orders
And so on. The site collection admin might add another link between the "Active" and "Inactive" links. When the feature is deactivated I don't want to remove the items, but if the feature is activated again i don't want the navigation to be added again :)
Is there a built in API that you can use? I know about the SPWeb.Navigation.QuickLaunch and the SPNavigationNode(Collection) structure etc. But is there another way?
Hope you can help :)
What kind of other way would you be looking for?
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
SPWeb web = (SPWeb)properties.Feature.Parent;
// Check for an existing link to the list.
SPNavigationNode listNode = web.Navigation.GetNodeByUrl(list.DefaultViewUrl);
// No link, so create one.
if (listNode == null)
{
// Create the node.
listNode = new SPNavigationNode(list.Title, list.DefaultViewUrl);
// Add it to Quick Launch.
web.Navigation.AddToQuickLaunch(listNode, SPQuickLaunchHeading.Lists);
}
}
We have used the method above for a while and it tends to work out just fine.
If you can let me know what kind of thing you are trying to accomplish that manipulating SPWeb.Navigation wont let you do, I might be able to be of some more help
This is a very minor behavior when compared with the entire scope, but it is one that I'd like to put a stop to.
I have created a very, very simple SharePoint Feature. It has two elements in its manifest: an aspx webpart page, and an elements xml. I'll paraphrase my elements xml, which just adds a module, below.
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="Pass" Url="" Path="">
<File Url="pasq.aspx" NavBarHome="True" Type="Ghostable">
<AllUsersWebPart WebPartZoneID="Left" WebPartOrder="0">
<![CDATA[
WARGH THIS PART DOESN'T MATTER FOR THIS QUESTION!
]]>
</AllUsersWebPart>
</File>
</Module>
</Elements>
Now, on the first time I deploy and activate this feature, it works properly. But if I have to deactivate and then reactivate the feature in order to fix some properties in the webpart, then I find myself with a second webpart on the page. Naturally, each similar cycle will just add more and more. I understand that a new webpart must be created in order to employ the changes I just made, but why is the old webpart still on the page? Can I make this older webpart go away automatically as part of the feature activation/deactivation process without needing to employ a Receiver class?
EDIT
Based on the fact that I've already employed a Receiver to solve this issue, I ended up just adding the webpart removal as part of feature deactivation. But I was still hoping that maybe there's something I'm missing.
Unfortunately the content will be repeated unless you do one of the following :
Solution 1 : you can add a similar code to the activate feature to add webparts in the pages as follows:
SPFile file = page.File;
using (SPLimitedWebPartManager WebPartManager =
file.GetLimitedWebPartManager(PersonalizationScope.Shared))
{
try
{
SPLimitedWebPartCollection webparts = WebPartManager .WebParts;
if (webparts.Count == 0)
{
//set your web part
WebPartManager.AddWebPart(webpart, "MomoZone", 1);
}
}
catch(Exception ex)
{
throw;
}
finally
{
WebPartManager.Web.Dispose();
}
}
Solution 2 : you can use site definition with and Onet file that runs only once the site is created so you would have no more of this problem while setting your efatures to hidden
Cheers
SharePoint doesn't offer a declarative way (xml) to remove feature elements automagically. You'll have to add some code in a FeatureDeactiving method on the feature receiver to remove it programatically, sorry.
I have not worked with webparts for sharepoint before, but need to make change to a webpart, that needs to be propagated to some 700 websites. It is a change to one of the properties of the webpart, the value needs to be changed. Is there a way to get metadata for a webpart and change it directly in the database (I assume that is where it is stored.)?
Here is the scenario: Webpart contains a comma delimited list of document types (internal type) that it should display. Now there are new doc. types that need to be added to all 700 websites. I need a way to enumerate websites, get the webpart metadata, and add these new doc types to webpart. Currently they go manually to each website, click on edit, type in new doc type, and save it.
As others have said the correct approach is to programmatically achieve this rather than edit the content database which will make your installation unsupportable. I regularly use a console application to do this in a site collection made up of sites created from a site template.
Here is an example that changes the Title property of a ListViewWebPart. Updated to include code for recursive loop. I haven't tested this but it should work.
private static void ProcessSiteCollection(string url)
{
using (SPSite siteCollection = new SPSite(url))
{
SPWeb rootWeb = siteCollection.RootWeb;
ProcessWebs(rootWeb);
}
}
private static void ProcessWebs(SPWeb parentWeb)
{
foreach (SPWeb web in parentWeb.Webs)
{
try
{
UpdateWebPart(web); // Set web part properties
ProcessWebs(web); // Recursively loop through children
}
finally
{
web.Dispose();
}
}
}
private static void UpdateWebPart(SPWeb web)
{
using (SPLimitedWebPartManager webPartManager =
web.GetLimitedWebPartManager("default.aspx", PersonalizationScope.Shared))
{
try
{
foreach (WebPart webPart in webPartManager.WebParts)
{
if (webPart.Title == "My Web Part")
{
ListViewWebPart listViewWebPart = (ListViewWebPart)webPart;
listViewWebPart.Title = "Updated Web Part";
webPartManager.SaveChanges(listViewWebPart);
web.Update();
break;
}
}
}
finally
{
webPartManager.Web.Dispose();
}
}
}
Directly accessing the sharepoint content databases is a big "no no." That's the official answer. :)
That being said, I have only ever looked in the content databases and never tried to actually change anything manually.
My suggestion, would be to modify the existing web part to modify the property based on currently set property(s). (I am assuming that some currently set property is invalid or needs to be updated based on changes to the infrastructure.) ... If this is the case, you can validate the property; making sure that current property is changed to what it needs to be, and/or making sure future property changes are valid.
Good luck!
DON'T
Seriously, do not go into the content databases and edit it. That way you are not supported anymore if anything should happen and Microsoft will not support you anymore (not until you revert the database back to an earlier version from a backup that is).
You can use the API to access webparts in your sites, here's some code that should get you started:
Enumerate page webparts
I have a webpart that displays HTML output within the RenderWebPart method and also creates controls within the CreateChildControls both methods are declared as overridden in the webpart.
My question is how to I control the order of the display of the controls and html output?
At the moment I call EnsureChildControls() within the RenderWebPart mthod to ensure all controls within the CreateChildControls are created and then the html out is rendered.
What if I wanted to display a control on the page then html output and then another control below in that order?
I would recommend moving all of your static HTML out of the Render function and into the CreateChildControls function. If you need to, you can add regular old HTML using Labels, WebControls, or even better... LiteralControls. Then you can just add them to your Controls collection.
Example:
WebControl container = new WebControl(System.Web.UI.HtmlTextWriterTag.Div);
StringBuilder sb = new StringBuilder();
sb.Append("<ul>");
foreach (Node child in this.Children)
{
sb.AppendFormat("<li>{1}</li>", url, name);
}
sb.Append("</ul>");
LiteralControl l = new LiteralControl();
l.Text = sb.ToString();
container.Controls.Add(l);
I'm actually relatively new to SharePoint and I was very astounded initially that one had to create individual controls manually, without a GUI interface like any normal ASP.NET web development involves. However, I had a very very good tip that allowed me to still use a GUI interface, and use it to create webparts for Sharepoint. It involves wrapping WebUserControls in webparts.
The link I used is here: http://www.a2zdotnet.com/View.aspx?id=95
You basically create WebUserControls (.ascx file) in a website, and so you can just add your controls like any normal .aspx page. You can also have a normal code-behind file (.ascx.cs). You then drag the .ascx file onto an .aspx file, so that it will then use your WebUserControl. When your .ascx files are ready and built, you copy them to the Layouts directory in the 12-Hive of your SharePoint Server. Best to create a sub-directory in there, to avoid clashing with other files in there already.
You then need to create a separate class Library project, that will have your WebPart code on. You then tell your WebPart to use your .ascx files in the layouts directory. Something like this:
protected override void CreateChildControls()
{
base.CreateChildControls();
try
{
this.Controls.Clear();
_myControl = this.Page.LoadControl("\\_layouts\\MyFolder\\WebUserControl.ascx");
this.Controls.Add(_myControl);
}
catch (Exception e)
{
err = e.Message;
}
The link I provided above provides more information, but basically you compile the webpart project and then add the DLL to the BIN directory of your sharepoint server (c:\inetpub\wwwroot\wss\ etc). You don't have to compile it to the GAC, by the way.
Then you add a entry to the web.config of your sharepoint server:
<SafeControl Assembly="MyWebUserControl, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9f4da00116c38ec5" Namespace="MyWebUserControl" TypeName="*" Safe="True" />
If you didn't compile the DLL to the GAC, but the BIN instead, you just need:
<SafeControl Assembly="UserControl" Namespace="UserControl" TypeName="*" Safe="True" />
Again, the link I posted above has it written in the code, a reference to the GUID, which is only needed if you placed the DLL in the GAC. You don't need the GUID part of the code if you only placed in the BIN directory.
Hope that helps.
Ash