Filter Custom Image Selector by Album?
Hey Telerik folks (specifically Ivan),
Really enjoyed this post and am happily moving along with images in both blogs and news. I was wondering, though, if there was an easy method of filtering those images shown by album. In other words, I have tens of images in the system and don't really want or need to see them when picking an image for blog or news posts. All I really need is to be able to see images from a particular album in the picker.
Anyway, any easy method for implementing that?
Thanks
- William
I'm assuming, perhaps, this is a FilterExpression applied to the GenericCollectionBinder that specifies an album filter on the image side. Just don't know what the filter should look like.
Thanks!
I got it:
FilterExpression="Visible = true AND Status = Live AND Album.Id=6DB14A9E-880C-4C8C-9FAF-06D7D71EC6FC"
Powerful stuff, though it seems a bit fragile. Is there a better way to handle this filtering?
Thanks.
- William
Hi William,
If you use the property from the designer, this is the only way to do it. We are going to create a topic about how to use this property FilterExpression, because currently it is not officially documented and there are only samples on the forums.
Regards,
Ivan Dimitrov
the Telerik team
Thanks, Ivan.
For now, that approach works for me. Looking forward to the continuation of your blog posts, though.
Great stuff!
- William
hey guys,
I went through the Custom Image selector and added the option to filter by album. I default to showing the selected image and a drop down of all albums. Choosing an album hides the selected image and shows all images from that list. i plan to upload it to the marketplace for everyone to get. it currently only supports one instance on the form at a time But i'll give you the basic idea to enable the Image selector to allow filtering by an album. It should be all prettied up and submitted to the marketplace sometime next week, but I hate waiting around indefinitely on stuff so.. here you go
Please note that I moved everything to the ImageSelector folder and adjusted the namespaces accordingly
(part of file) SimpleImageField.ascx OnClientShow and OnClientClose were added to the rad window and and a class called txtForImageField was added to textbox.
<
telerik:RadWindowManager
ID
=
"windowManager"
runat
=
"server"
Skin
=
"Sitefinity"
>
<
Windows
>
<
telerik:RadWindow
ID
=
"simpleImageSelector"
Width
=
"600"
Height
=
"400"
NavigateUrl
=
"~/Sitefinity/Dialog/SimpleImageSelectorDialog"
runat
=
"server"
ReloadOnShow
=
"true"
Modal
=
"true"
VisibleStatusbar
=
"false"
Behaviors
=
"Close"
OnClientShow
=
"OnImageSelectorClientShow"
OnClientClose
=
"OnImageSelectorClientClose"
>
</
telerik:RadWindow
>
</
Windows
>
</
telerik:RadWindowManager
>
<
asp:TextBox
ID
=
"textBox"
runat
=
"server"
CssClass
=
"sfTxt txtForImageField"
/>
$ = jQuery;
var
_previousItem =
null
;
function
OnImageSelectorClientShow(sender, args)
if
($(
".txtForImageField"
).length > 0 && $(
".txtForImageField"
).val().length > 0)
_previousItem = $(
".txtForImageField"
).val();
if
(_previousItem !=
null
)
sender.argument = _previousItem;
function
OnImageSelectorClientClose(sender, args)
_previousItem =
null
;
<%@ Control Language="C#" AutoEventWireup="true" %>
<%@ Register Assembly="Telerik.Sitefinity" Namespace="Telerik.Sitefinity.Web.UI" TagPrefix="sitefinity" %>
<
sitefinity:ResourceLinks
ID
=
"resourcesLinks"
runat
=
"server"
>
<
sitefinity:ResourceFile
JavaScriptLibrary
=
"JQuery"
>
</
sitefinity:ResourceFile
>
</
sitefinity:ResourceLinks
>
<
select
name
=
"albumList"
id
=
"albumList"
class
=
"albumList"
runat
=
"server"
>
</
select
><
img
alt
=
"loading albums.."
class
=
"loadingAlbums"
src
=
"/images/loading.gif"
/>
<
ul
id
=
"imageList"
class
=
"imageList"
runat
=
"server"
>
</
ul
><
img
alt
=
"loading images.."
class
=
"loadingImages"
style
=
"display:none;"
src
=
"/images/loading.gif"
/>
<
sitefinity:GenericCollectionBinder
ID
=
"albumListBinder"
runat
=
"server"
TargetId
=
"albumList"
ServiceUrl
=
"~/Sitefinity/Services/Content/AlbumService.svc/"
BindOnLoad
=
"false"
DataKeyNames
=
"Id"
DataMembers
=
"Id, Title"
OnClientDataBound
=
"OnAlbumsDataBound"
>
<
Containers
>
<
sitefinity:BinderContainer
ID
=
"selectOptionContainer"
runat
=
"server"
TemplateHolderTag
=
"Select"
RenderContainer
=
"false"
>
<
option
sys:value
=
"Id"
>Title</
option
>
</
sitefinity:BinderContainer
>
</
Containers
>
</
sitefinity:GenericCollectionBinder
>
<
sitefinity:GenericCollectionBinder
ID
=
"imageListBinder"
runat
=
"server"
TargetId
=
"imageList"
ServiceUrl
=
"~/Sitefinity/Services/Content/ImageService.svc/"
FilterExpression
=
"Parent.Id == 00000000-0000-0000-0000-000000000000"
OnClientDataBound
=
"OnImagesDataBound"
BindOnLoad
=
"false"
DataKeyNames
=
"Id"
DataMembers
=
"Id, Title, ThumbnailUrl"
CssClass
=
"imageListBinder"
>
<
Containers
>
<
sitefinity:BinderContainer
ID
=
"imageTitleContainer"
runat
=
"server"
RenderContainer
=
"false"
>
<
li
><
div
style
=
"float:left; margin: 10px 10px 10px 10px; padding: 10px"
>
<
a
href
=
"javascript:void(0);"
class
=
"sf_binderCommand_selectImage"
>
<
img
sys:src
=
"ThumbnailUrl"
/>
</
a
>
<
div
>Title</
div
>
</
div
>
</
li
>
</
sitefinity:BinderContainer
>
</
Containers
>
</
sitefinity:GenericCollectionBinder
>
<
div
class
=
"previewImage"
style
=
"display:none;"
>
<
h3
>Current Image</
h3
>
<
img
alt
=
"preview"
src
=
""
/>
</
div
>
Type.registerNamespace(
"SitefinityWebApp.ImageSelector"
);
SitefinityWebApp.ImageSelector.SimpleImageSelector =
function
(element)
SitefinityWebApp.ImageSelector.SimpleImageSelector.initializeBase(
this
, [element]);
this
._binder =
null
;
this
._binderAlbum =
null
;
this
._onLoadDelegate =
null
;
this
._onUnloadDelegate =
null
;
this
._binderCommandDelegate =
null
;
this
._binderAlbumCommandDelegate =
null
;
this
._selectedImageUrl =
null
;
SitefinityWebApp.ImageSelector.SimpleImageSelector.prototype =
initialize:
function
()
SitefinityWebApp.ImageSelector.SimpleImageSelector.callBaseMethod(
this
,
"initialize"
);
this
._onLoadDelegate = Function.createDelegate(
this
,
this
._onLoad);
Sys.Application.add_load(
this
._onLoadDelegate);
this
._onUnloadDelegate = Function.createDelegate(
this
,
this
._onUnload);
Sys.Application.add_unload(
this
._onUnloadDelegate);
this
._binderCommandDelegate = Function.createDelegate(
this
,
this
._binderCommand);
this
._binderAlbumCommandDelegate = Function.createDelegate(
this
,
this
._binderAlbumCommand);
,
dispose:
function
()
SitefinityWebApp.ImageSelector.SimpleImageSelector.callBaseMethod(
this
,
"dispose"
);
Sys.Application.remove_load(
this
._onLoadDelegate);
if
(
this
._onLoadDelegate)
delete
this
._onLoadDelegate;
Sys.Application.remove_load(
this
._onUnloadDelegate);
if
(
this
._onUnloadDelegate)
delete
this
._onUnloadDelegate;
,
/* -------------------- public methods ------------ */
/* -------------------- events -------------------- */
/* -------------------- event handlers ------------ */
_onLoad:
function
(sender, args)
this
.get_binder().add_onItemCommand(
this
._binderCommandDelegate);
this
.get_binderAlbum().add_onItemCommand(
this
._binderAlbumCommandDelegate);
this
.get_binder().DataBind();
this
.get_binderAlbum().DataBind();
,
_onUnload:
function
(sender, args)
this
.get_binder().remove_onItemCommand(
this
._binderCommandDelegate);
this
.get_binderAlbum().remove_onItemCommand(
this
._binderAlbumCommandDelegate);
,
_binderCommand:
function
(sender, args)
if
(args.get_commandName() ==
"selectImage"
)
var
imageUrl = args.get_dataItem().ThumbnailUrl;
this
.set_selectedImageUrl(imageUrl);
$(
".previewImage img"
).attr(
"src"
, imageUrl);
// remove class from previously selected images
var
selected = jQuery(args.get_itemElement().parentNode).find(
"li.sf_selectedImage"
).each(
function
(index, element)
jQuery(element).removeClass(
"sf_selectedImage"
);
);
// set class to currently selected image
jQuery(args.get_itemElement()).addClass(
"sf_selectedImage"
);
,
_binderAlbumCommand:
function
(sender, args)
,
/* -------------------- private methods ----------- */
/* -------------------- properties ---------------- */
get_binder:
function
()
return
this
._binder;
,
set_binder:
function
(value)
this
._binder = value;
,
get_binderAlbum:
function
()
return
this
._binderAlbum;
,
set_binderAlbum:
function
(value)
this
._binderAlbum = value;
,
get_selectedImageUrl:
function
()
return
this
._selectedImageUrl;
,
set_selectedImageUrl:
function
(value)
this
._selectedImageUrl = value;
;
SitefinityWebApp.ImageSelector.SimpleImageSelector.registerClass(
"SitefinityWebApp.ImageSelector.SimpleImageSelector"
, Sys.UI.Control);
$ = jQuery;
var
_theImageBinder =
null
;
var
_previousItem =
null
;
function
OnAlbumsDataBound(sender, commandArgs)
$(
".albumList"
).prepend(
"<option value=''>Choose an Album</value>"
)
$(
".albumList"
).val(
""
);
$(
".loadingAlbums"
).hide();
function
OnImagesDataBound(sender, commandArgs)
_theImageBinder = sender;
$(
".loadingImages"
).hide();
function
GetRadWindow()
var
oWindow =
null
;
if
(window.radWindow)
oWindow = window.radWindow;
else
if
(window.frameElement.radWindow)
oWindow = window.frameElement.radWindow;
return
oWindow;
var
_initialLoad =
true
;
function
pageLoad()
if
(_initialLoad)
var
currentWindow = GetRadWindow();
if
(currentWindow.argument !=
null
&& currentWindow.argument.length > 0)
$(
".previewImage img"
).attr(
"src"
, currentWindow.argument);
$(
".previewImage"
).show();
else
$(
".previewImage"
).hide();
_initialLoad =
false
;
$(document).ready(
function
()
$(
".albumList"
).change(
function
()
if
($(
this
).val() !=
""
)
$(
".previewImage"
).hide();
$(
".loadingImages"
).show();
if
(_theImageBinder !=
null
)
_theImageBinder.set_filterExpression(
"Parent.Id == "
+ $(
".albumList"
).val());
_theImageBinder.DataBind();
$(
".imageList"
).show();
else
alert(
'Binder not found'
);
else
$(
".imageList"
).hide();
$(
".previewImage"
).show();
);
);
//dg this property is new
/// <summary>
/// Gets a reference to the RadListViewBinder control in the template
/// </summary>
protected
virtual
GenericCollectionBinder BinderAlbum
get
return
this
.Container.GetControl<GenericCollectionBinder>(
"albumListBinder"
,
true
);
public
override
IEnumerable<ScriptDescriptor> GetScriptDescriptors()
var descriptors =
new
List<ScriptDescriptor>();
var descriptor =
new
ScriptControlDescriptor(
typeof
(SimpleImageSelector).FullName,
this
.ClientID);
//dg Added
descriptor.AddComponentProperty(
"binderAlbum"
,
this
.BinderAlbum.ClientID);
descriptor.AddComponentProperty(
"binder"
,
this
.Binder.ClientID);
descriptors.Add(descriptor);
return
descriptors.ToArray();
Hi Drew Greenwell,
Thanks a lot for sharing this with the community. We may highlight it in a blog post once you've uploaded it to the marketplace. Please update this thread when you do.
@William Apart from the Filter expression, another way is to change the service URL of the binder, depending on the album. There's a method in the ImageService to get child items. The URL to invoke it is:
~/Sitefinity/Services/Content/ImageService.svc/parent/albumId
Just replace albumId with the real Guid. You can use the set_serviceBaseUrl() method of the binder for this.
I keep getting the following error when using the GenericCollectionBinder to populate the album list:
'System.Web.UI.HtmlControls.HtmlSelect' does not allow child controls.
I have also looked at the following page, http://www.sitefinity.com/40/help/developers-guide/deep-dive-client-side-programming-client-binder-controls-generic-collection-binder.html, which also uses a select element to be bound to.
If I change the select element to a div element and run the control, it will render option elements as child elements of the div element. No other elements are present so I don't understand the error.
The site is running on version 4.0.1210. Any thoughts??
Edit::
Stack Trace if it helps
[HttpException (0x80004005): 'System.Web.UI.HtmlControls.HtmlSelect' does not allow child controls.]
System.Web.UI.EmptyControlCollection.ThrowNotSupportedException() +108
System.Web.UI.EmptyControlCollection.Add(Control child) +4
Telerik.Sitefinity.Web.UI.Templates.RootBuilder.CreateChildControls(Control parent, Control bindingContainer, PlaceHoldersCollection placeHolders) +579
Telerik.Sitefinity.Web.UI.Templates.ObjectBuilder.CreateObject(Control bindingContainer, PlaceHoldersCollection placeHolders) +766
Telerik.Sitefinity.Web.UI.Templates.ControlBuilder.CreateControl(Control bindingContainer, PlaceHoldersCollection placeHolders) +18
Telerik.Sitefinity.Web.UI.Templates.ControlBuilder.CreateControl(Control bindingContainer) +12
Telerik.Sitefinity.Web.UI.Templates.RootBuilder.CreateChildControls(Control parent, Control bindingContainer) +269
Telerik.Sitefinity.Web.UI.StringTemplate.InstantiateIn(Control container, PlaceHoldersCollection placeHolders) +88
Telerik.Sitefinity.Web.UI.StringTemplate.InstantiateIn(Control container) +7
Telerik.Sitefinity.Web.UI.SimpleView.CreateContainer(ITemplate template) +35
Telerik.Sitefinity.Web.UI.SimpleView.get_Container() +33
Telerik.Sitefinity.Web.UI.SimpleView.CreateChildControls() +46
System.Web.UI.Control.EnsureChildControls() +102
System.Web.UI.Control.PreRenderRecursiveInternal() +42
System.Web.UI.Control.PreRenderRecursiveInternal() +175
System.Web.UI.Control.PreRenderRecursiveInternal() +175
System.Web.UI.Control.PreRenderRecursiveInternal() +175
System.Web.UI.Control.PreRenderRecursiveInternal() +175
System.Web.UI.Control.PreRenderRecursiveInternal() +175
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +2496
Hello Richard Baugh,
Can you ensure that you have runat="server" on the select tag? That's the only thing from the top of my head, I'm not sure why you are getting this error. Please send your whole control (and template) and I'll try to debug it and help more.
Regards,Yes, it has runat="server" as an attribute. I will open a support ticket to upload the project.