tag:blogger.com,1999:blog-74486228901700414142024-02-20T23:22:16.943+02:00.Net TipsIgor Mazurhttp://www.blogger.com/profile/14104591481669577402noreply@blogger.comBlogger3125tag:blogger.com,1999:blog-7448622890170041414.post-45221261374531661252008-06-11T17:09:00.001+03:002008-06-11T17:09:22.513+03:00Opening web part’s editor zone in dialog window (improvements)<p></p> <p></p> <p>For first, this idea and main peice of code had been taken from <a href="http://markitup.com/Posts/Post.aspx?postId=a20e70f2-7eb1-40b6-b498-5bf4cb7b6edd">Darren Neimke's blog post</a>. But after some experiments I found that this method can be little improved:</p> <p><em>In original post only one user in same time can edit web part's properties</em></p> <p>To do this, it is logical to change PersonalizationScope from Shared to User in our DialogEditor.aspx.</p> <blockquote> <p><platform:CustomWebPartManager runat="server" id="WebPartManager1"> <br />    <Personalization InitialScope="<strong>User</strong>" ProviderName="AspNetSqlPersonalizationProvider"></Personalization> <br /></platform:CustomWebPartManager> </p> </blockquote> <p>BUT with current implementation we will find that in method <strong>ExportWebPart</strong>, when we are receiving exported XML from DialogEditor, in line after receiving WebPartManager is still ….. Shared….</p> <blockquote> <p>public static string ExportWebPart(string wpid, string path, HttpContext context) <br />        { <br />            StringBuilder sb = new StringBuilder(); </p> <p>            string virtualPath = path; <br />            if (virtualPath.Contains("?")) <br />            { <br />                virtualPath = virtualPath.Substring(0, virtualPath.IndexOf("?")); <br />            } </p> <p>            Page page = (Page)BuildManager.CreateInstanceFromVirtualPath(virtualPath, typeof(Page)); </p> <p>            page.Load += delegate <br />            { <br />                <strong>WebPartManager wm = WebPartManager.GetCurrentWebPartManager(page);</strong> </p> <p>                if (wm.WebParts.Count > 0) <br />                { <br />                    WebPart part; <br />                    if (wpid != null) <br />                        part = wm.WebParts[wpid]; <br />                    else <br />                        part = wm.WebParts[0]; </p> <p>                    if (part.ExportMode != WebPartExportMode.None) <br />                    { <br />                        using (StringWriter sw = new StringWriter(sb)) <br />                        using (XmlTextWriter xw = new XmlTextWriter(sw)) <br />                        { <br />                            wm.ExportWebPart(part, xw); <br />                        } <br />                    } <br />                } <br />            }; </p> <p>            ExecutePage(page, path, context); <br />            return sb.ToString(); <br />        }</p> </blockquote> <p>This problem occurs because we use Execute method that is store previous http context and previous page with WebPartManager placed on this page, and if you look with Reflector to <b>PersonalizationProvider’s</b>  method <strong>DetermineInitialScope</strong> you will find next code:</p> <blockquote> <p>    else if <strong>((page.PreviousPage != null) && !page.PreviousPage.IsCrossPagePostBack)</strong> <br />       { <br />           WebPartManager currentWebPartManager = WebPartManager.GetCurrentWebPartManager(page.PreviousPage); <br />           if (currentWebPartManager != null) <br />           { <br />               <strong>initialScope = currentWebPartManager.Personalization.Scope;</strong> <br />           } <br />       } </p> </blockquote> <p></p> <p></p> <p></p> <p></p> <p></p> <p></p> <p></p> <p></p> <p></p> <p>So – we use PersonalizationScope of WebPartManager from previous page.</p> <p>To avoid this – we can use many way, write custom PersonalizationProvider for this DialogEditor.asxp page or write, for example, webservice that will do exactly same actions but of course does not contains any WebPartManagers. Last way is shown bottom.</p> <blockquote> <p>[WebService(Namespace = "<a href="http://tempuri.org/")]">http://tempuri.org/")]</a> <br />    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] <br />    [ToolboxItem(false)] <br />    // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. <br />    // [System.Web.Script.Services.ScriptService] <br />    public class AdminWS : WebService <br />    { </p> <p>        [WebMethod(true)] <br />        public string GetWPXml() <br />        { <br />            StringBuilder sb = new StringBuilder(); <br />            Page page = (Page)BuildManager.CreateInstanceFromVirtualPath("~/DialogEditor.aspx", typeof(Page)); </p> <p>            page.Load += delegate <br />            { <br />                WebPartManager wm = WebPartManager.GetCurrentWebPartManager(page); </p> <p>                if (wm.WebParts.Count > 0) <br />                { <br />                    WebPart part; <br />                    part = wm.WebParts[0]; </p> <p>                    if (part.ExportMode != WebPartExportMode.None) <br />                    { <br />                        using (StringWriter sw = new StringWriter(sb)) <br />                        using (XmlTextWriter xw = new XmlTextWriter(sw)) <br />                        { <br />                            wm.ExportWebPart(part, xw); <br />                        } <br />                    } <br />                } <br />            }; </p> <p>            ExecutePage(page, "~/DialogEditor.aspx", this.Context); <br />            return sb.ToString(); <br />        } </p> <p>        private static void ExecutePage(Page page, string path, HttpContext context) <br />        { <br />            string originalPath = context.Request.Path; <br />            context.RewritePath(path); <br />            context.Server.Execute(page, TextWriter.Null, false); <br />            context.RewritePath(originalPath); <br />        } </p> <p>    }</p> </blockquote> <p></p> <p></p> <p></p> <p>And we should make some changes in RaisePostBackEvent method of ours webpart:</p> <blockquote> <p>        public void RaisePostBackEvent(string eventArgument) <br />        { <br />            if (eventArgument == "whatever") <br />            { <br />                <strong>InternalAdminWS.AdminWS adminws = new InternalAdminWS.AdminWS(); <br />                adminws.CookieContainer = new CookieContainer(); <br />                adminws.CookieContainer.Add(new Cookie(FormsAuthentication.FormsCookieName, Request.Cookies[FormsAuthentication.FormsCookieName].Value, Request.Cookies[FormsAuthentication.FormsCookieName].Path, Request.Url.Host)); <br />                adminws.Url = Request.Url.Scheme + "://" + Request.Url.Authority + "/AdminWS.asmx"; <br />                adminws.Credentials = CredentialCache.DefaultNetworkCredentials; <br />                string xml = adminws.GetWPXml(); <br /></strong>                //string xml = WebPartHelper.ExportWebPart(null, "~/DialogEditor.aspx", this.Context); <br />                if (!string.IsNullOrEmpty(xml)) <br />                { <br />                    WebPartHelper.CopyWebPartValues(xml, this); <br />                } <br />            } <br />        }</p> </blockquote> <p>Also I had make little changes to original CopyWebPartValues method – because when webpart properties are exported within ExportWebPart method – it use TypeConverter’s so to avoid custom conversion of every property type I had changed this method:</p> <p></p> <p></p> <p></p> <p></p> <p></p> <blockquote> <p>public static void CopyWebPartValues(string webPartXML, Control copyTo) <br />{ </p> <p>    XmlDocument doc = new XmlDocument(); <br />    doc.LoadXml(webPartXML); <br />    XmlNamespaceManager mgr = new XmlNamespaceManager(doc.NameTable); <br />    mgr.AddNamespace("wp", "<a href="http://schemas.microsoft.com/WebPart/v3");">http://schemas.microsoft.com/WebPart/v3");</a></p> <p>    foreach (XmlNode node in doc.SelectNodes(@"//wp:data/wp:properties/wp:property", mgr)) <br />    { <br />        string propertyName = node.Attributes["name"].Value; <br />        string propertyValue = (node.FirstChild == null) ? "" : node.FirstChild.Value; <br />        PropertyInfo prop = copyTo.GetType().GetProperty(propertyName); </p> <p>        Type propertyType = prop.PropertyType; </p> <p>        // do we have a nullable type? <br />        if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) <br />        { <br />            NullableConverter nc = new NullableConverter(propertyType); <br />            propertyType = nc.UnderlyingType; <br />        } </p> <p>        TypeConverter converter = null; <br />        if (prop != null) <br />        { <br />            TypeConverterAttribute attribute = Attribute.GetCustomAttribute(prop, typeof(TypeConverterAttribute), true) as TypeConverterAttribute; <br />            if (attribute != null) <br />            { <br />                Type type = BuildManager.GetType(attribute.ConverterTypeName, false); </p> <p>                if ((type != null) && type.IsSubclassOf(typeof(TypeConverter))) <br />                { <br />                    TypeConverter converter2 = (TypeConverter)Activator.CreateInstance(type); <br />                    if (converter2.CanConvertFrom(typeof(string)) && converter2.CanConvertTo(typeof(string))) <br />                    { <br />                        converter = converter2; <br />                    } <br />                } <br />            } <br />        } <br />        if (converter == null) <br />        { <br />            TypeConverter converter3 = TypeDescriptor.GetConverter(propertyType); <br />            if (converter3.CanConvertFrom(typeof(string)) && converter3.CanConvertTo(typeof(string))) <br />            { <br />                converter = converter3; <br />            } <br />        } <br />        try <br />        { <br />            object result = converter.ConvertFrom(propertyValue); <br />            prop.SetValue(copyTo, result, null); <br />        } catch (Exception ex) <br />        { <br />            log.Info(string.Format("Can't parse property type {0} with name {1}/nWith Exception : {2}", prop.PropertyType.Name, propertyName, ex)); <br />        } <br />    } <br />}</p></blockquote> Igor Mazurhttp://www.blogger.com/profile/14104591481669577402noreply@blogger.com0tag:blogger.com,1999:blog-7448622890170041414.post-24347745283651391822007-11-06T15:15:00.000+02:002007-11-06T21:06:39.610+02:00Workaround for absence of semi-additive function LastNonEmpty in SQL Server 2005 Standard Edition<p>In my last project I'd met problem when I tried to setup OLAP cube with semi-additive measure on Analysis Service Standard Edition. In my case LastNonEmpty.</p> <p>As example database I use Adventure Works DW.</p> <p>Measure [Last NonEmpty Total Product Cost - Workaround] was created under [Sales Summary] measure group with Sum aggregate function. Then in Calculations added next cell calculation member.</p> <p> </p> <div><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none">CREATE CELL CALCULATION [Adventure Works].CalculateLastNonEmpty
FOR <span style="color: #006080">'({[Measures].[Last NonEmpty Total Product Cost - Workaround]}, DESCENDANTS([Date].[Calendar].[All Periods]))'</span>
AS
<span style="color: #006080">'
Aggregate(
Tail(
Filter(
Descendants(
[Date].[Calendar].CurrentMember,
[Date].[Calendar].[Date]
) ,
CalculationPassValue([Measures].[Last NonEmpty Total Product Cost - Workaround], 0) <> null
),
1
),
CalculationPassValue([Measures].[Last NonEmpty Total Product Cost - Workaround], 0)
) '</span> </pre></div>
<p>After this result of [Measures].[Last NonEmpty Total Product Cost - Workaround] will be similar LastNonEmpty aggregate function. </p> Igor Mazurhttp://www.blogger.com/profile/14104591481669577402noreply@blogger.com3tag:blogger.com,1999:blog-7448622890170041414.post-14943643705063130652007-10-19T23:23:00.001+03:002007-10-20T00:03:40.597+03:00Customizing EntityEditorWithPicker<p>Everyone, who use Sharepoint had seen PeopleEditor control. It very useful in many cases - for example in permission forms: <br><a href="http://lh5.google.com/igor.kozlov/RxkSS-_M74I/AAAAAAAAAB0/yiDl5WwmdDo/PeopleEditor%5B3%5D.png"><img id="id" style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="386" alt="PeopleEditor" src="http://lh5.google.com/igor.kozlov/RxkST-_M75I/AAAAAAAAAB8/nnl2uZqPYAk/PeopleEditor_thumb%5B1%5D.png" width="430" border="0"></a></p> <p> </p> <p>But this control can be used not only for looking for users or groups. You can use its base class EntityEditorWithPicker and extend it to search any data. I will show you very simple example how to do this.</p> <p>We should extend three classes: </p> <div><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #0000ff">using</span> System;
<span style="color: #0000ff">using</span> System.Collections.Generic;
<span style="color: #0000ff">using</span> System.Text;
<span style="color: #0000ff">using</span> Microsoft.SharePoint.WebControls;
<span style="color: #0000ff">namespace</span> EntityEditorTest
{
<span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> CustomEntityEditor : EntityEditorWithPicker
{
<span style="color: #0000ff">protected</span> <span style="color: #0000ff">override</span> <span style="color: #0000ff">void</span> OnInit(EventArgs e)
{
<span style="color: #0000ff">base</span>.OnInit(e);
PickerDialogType = <span style="color: #0000ff">typeof</span> (CustomPickerDialog);
}
<span style="color: #0000ff">public</span> <span style="color: #0000ff">override</span> PickerEntity ValidateEntity(PickerEntity needsValidation)
{
<span style="color: #0000ff">if</span> (needsValidation.Key.Equals(<span style="color: #006080">"igor"</span>, StringComparison.InvariantCultureIgnoreCase))
{
needsValidation.DisplayText = <span style="color: #006080">"Igor Kozlov"</span>;
needsValidation.IsResolved = <span style="color: #0000ff">true</span>;
}
<span style="color: #0000ff">return</span> needsValidation;
}
}
}
</pre></div>
<div> </div>
<div><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #0000ff">using</span> System;
<span style="color: #0000ff">using</span> System.Collections;
<span style="color: #0000ff">using</span> System.Collections.Generic;
<span style="color: #0000ff">using</span> System.Text;
<span style="color: #0000ff">using</span> Microsoft.SharePoint.WebControls;
<span style="color: #0000ff">namespace</span> EntityEditorTest
{
<span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> CustomPickerDialog : PickerDialog
{
<span style="color: #0000ff">public</span> CustomPickerDialog()
: <span style="color: #0000ff">base</span>(<span style="color: #0000ff">new</span> CustomQueryControl(), <span style="color: #0000ff">new</span> TableResultControl(), <span style="color: #0000ff">new</span> CustomEntityEditor())
{
ArrayList columnDisplayNames = ((TableResultControl)<span style="color: #0000ff">base</span>.ResultControl).ColumnDisplayNames;
columnDisplayNames.Clear();
columnDisplayNames.Add(<span style="color: #006080">"Name"</span>);
columnDisplayNames.Add(<span style="color: #006080">"Description"</span>);
ArrayList columnNames = ((TableResultControl)<span style="color: #0000ff">base</span>.ResultControl).ColumnNames;
columnNames.Clear();
columnNames.Add(<span style="color: #006080">"Name"</span>);
columnNames.Add(<span style="color: #006080">"Description"</span>);
ArrayList columnWidths = ((TableResultControl)<span style="color: #0000ff">base</span>.ResultControl).ColumnWidths;
columnWidths.Clear();
columnWidths.Add(<span style="color: #006080">"30%"</span>);
columnWidths.Add(<span style="color: #006080">"70%"</span>);
}
}
}
</pre></div>
<div> </div>
<div><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: consolas, 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; border-bottom-style: none"><span style="color: #0000ff">using</span> System;
<span style="color: #0000ff">using</span> System.Collections.Generic;
<span style="color: #0000ff">using</span> System.Data;
<span style="color: #0000ff">using</span> System.Text;
<span style="color: #0000ff">using</span> Microsoft.SharePoint.WebControls;
<span style="color: #0000ff">namespace</span> EntityEditorTest
{
<span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> CustomQueryControl : SimpleQueryControl
{
<span style="color: #0000ff">public</span> CustomQueryControl()
{
Load += CustomQueryControl_Load;
}
<span style="color: #0000ff">void</span> CustomQueryControl_Load(<span style="color: #0000ff">object</span> sender, EventArgs e)
{
<span style="color: #0000ff">if</span> (!Page.IsPostBack)
{
EnsureChildControls();
mColumnList.Items.Add(<span style="color: #006080">"Name"</span>);
mColumnList.Items.Add(<span style="color: #006080">"Description"</span>);
}
}
<span style="color: #0000ff">protected</span> <span style="color: #0000ff">override</span> <span style="color: #0000ff">int</span> IssueQuery(<span style="color: #0000ff">string</span> search, <span style="color: #0000ff">string</span> groupName, <span style="color: #0000ff">int</span> pageIndex, <span style="color: #0000ff">int</span> pageSize)
{
DataTable dummyTable = GetDummyTable();
PickerDialog.Results = dummyTable;
PickerDialog.ResultControl.PageSize = dummyTable.Rows.Count;
<span style="color: #0000ff">return</span> dummyTable.Rows.Count;
}
<span style="color: #0000ff">private</span> DataTable GetDummyTable()
{
DataTable dummyTable = <span style="color: #0000ff">new</span> DataTable();
dummyTable.Columns.Add(<span style="color: #006080">"Name"</span>);
dummyTable.Columns.Add(<span style="color: #006080">"Description"</span>);
DataRow row1 = dummyTable.NewRow();
row1[<span style="color: #006080">"Name"</span>] = <span style="color: #006080">"Name 1"</span>;
row1[<span style="color: #006080">"Description"</span>] = <span style="color: #006080">"Description 1"</span>;
dummyTable.Rows.Add(row1);
DataRow row2 = dummyTable.NewRow();
row2[<span style="color: #006080">"Name"</span>] = <span style="color: #006080">"Name 2"</span>;
row2[<span style="color: #006080">"Description"</span>] = <span style="color: #006080">"Description 2"</span>;
dummyTable.Rows.Add(row2);
<span style="color: #0000ff">return</span> dummyTable;
}
<span style="color: #0000ff">public</span> <span style="color: #0000ff">override</span> PickerEntity GetEntity(DataRow dr)
{
PickerEntity entity = <span style="color: #0000ff">new</span> PickerEntity();
entity.DisplayText = <span style="color: #006080">""</span> +dr[<span style="color: #006080">"Name"</span>];
entity.Key = <span style="color: #006080">""</span> + dr[<span style="color: #006080">"Name"</span>];
entity.Description = <span style="color: #006080">""</span> + dr[<span style="color: #006080">"Description"</span>];
entity.IsResolved = <span style="color: #0000ff">true</span>;
<span style="color: #0000ff">return</span> entity;
}
}
}
</pre></div>
<p>And there is result:</p>
<div><a href="http://lh5.google.com/igor.kozlov/RxkSU-_M76I/AAAAAAAAACE/BU9F4GjazlA/Custom%20Editor%5B7%5D.png"><img id="id" style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="413" alt="Custom Editor" src="http://lh4.google.com/igor.kozlov/RxkSVu_M77I/AAAAAAAAACM/Iv9ELjUjPHk/Custom%20Editor_thumb%5B5%5D.png" width="424" border="0"></a> </div> Igor Mazurhttp://www.blogger.com/profile/14104591481669577402noreply@blogger.com30