Add Items To Sitecore Combobox - c#

I'm creating a Sitecore Sheer UI wizard which has markup like this
<WizardFormIndent>
<GridPanel ID="FieldsAction" Columns="2" Width="100%" CellPadding="2">
<Literal Text="Brand:" GridPanel.NoWrap="true" Width="100%" />
<Combobox ID="Brand" GridPanel.Width="100%" Width="100%">
<!-- Leave empty as I want to populate available options in code -->
</Combobox>
<!-- Etc. -->
</WizardFormIndent>
But I cannot seem to find a way to add options to the combobox "Brand" in the code beside. Does anyone know how to finish the code below?
[Serializable]
public class MySitecorePage : WizardForm
{
// Filled in by the sheer UI framework
protected ComboBox Brands;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (!Context.ClientPage.IsEvent)
{
IEnumerable<Brand> brandsInSqlDb = GetBrands();
// this.Brands doesn't seem to have any methods
// to add options
}
}
}

First off, I'm assuming you're using the Sitecore Combobox from Sitecore.Web.UI.HtmlControls (and not the Telerik control for instance)?
Looking in Reflector, it end up doing something like this:
foreach (Control control in this.Controls)
{
if (control is ListItem)
{
list.Add(control);
}
}
So I'm expecting you'll need to build a loop through your brandsInSqlDb, instantiate a ListItem and add it to your Brands Combobox.Something like
foreach (var brand in brandsInSqlDb)
{
var item = new ListItem();
item.Header = brand.Name; // Set the text
item.Value = brand.Value; // Set the value
Brands.Controls.Add(item);
}

It should be lowercase B (Combobox not ComboBox). Full namespace is:
protected Sitecore.Web.UI.HtmlControls.Combobox Brands;
Then you can add options, e.g.:
ListItem listItem = new ListItem();
this.Brands.Controls.Add((System.Web.UI.Control) listItem);
listItem.ID = Sitecore.Web.UI.HtmlControls.Control.GetUniqueID("ListItem");
listItem.Header = name;
listItem.Value = name;
listItem.Selected = name == selectedName;

The way I do it is to 1st access the Combo box from the page:
ComboBox comboBox = Page.Controls.FindControl("idOfYourComboBox") as ComboBox
Now you got the access to the control you defined in your page. All now you have to do is to assign value to it:
foreach (var brand in brandsInSqlDb)
{
comboBox .Header = brand.Name; // Set the text
comboBox .Value = brand.Value; // Set the value
Brands.Controls.Add(item);
}

Related

Changing combobox item background color within wpf application

I have a wpf application in which I need to customize a combobox to make it editable.
public class AutoCompleteFacturation: ComboBox
{
List<vue_fsign_fiche_signaletique> liste = new List<vue_fsign_fiche_signaletique>();
// [...]
ItemsSource = NewDataSource;
liste = NewDataSource.ToList<vue_fsign_fiche_signaletique>();
ComboBoxItem item = (ComboBoxItem)base.Items[0];
item.Background = System.Windows.Media.Brushes.AliceBlue;
}
I need to change the background of the first item ComboBoxItem item = (ComboBoxItem)base.Items[0]; ==> I get an exception indicates that the cast of vue_fsign_fiche_signaletique to ComboBoxItem is not possible.
So How can I fix this to coloriate the first item of the combobox ?
Thanks
The item you're getting is the one that is bound using DataSource (here of type vue_fsign_fiche_signaletique).
What you need is ComboBoxItem, which is a container. To get it, use ItemContainerGenerator.ContainerFromIndex(index) or ItemContainerGenerator.ContainerFromItem(item):
https://msdn.microsoft.com/library/ms750552(v=vs.90).aspx
You binded your ComboBox on your list.
So Items return a list of vue_fsign_fiche_signaletique
2 Possibilities :
foreach (vue_fsign_fiche_signaletique fiche in liste)
{
ComboBoxItem i = new ComboBoxItem();
i.Content = fiche.Text;
i.Background = System.Windows.Media.Brushes.AliceBlue;
base.Items.Add(i);
}
Or wrap your vue_fsign_fiche_signaletique in a View Model and use a IValueConverter

Silverlight dependancy property is not notifying in custom control

Scenario
I have a custom combo box where i have a label in the Combobox selection box. I need to change the label as I noted in the second image. But I want to do it only when I select items by selecting the check box. I can select multiple items, so the label should be updated as a comma separated value of selected items. If there is not enough space to display the full text of the label there should be "..." symbol to indicate that there are more items selected in the combo box.
I created a custom Label by inheriting the text Box control where I do all the changes in the callback event of a Dependency property. (Check custom Text Box code)
Now the problem is that the callback event in the custom Text box control is not firing when I change the bounded property in the View model (I am doing this by adding values to the observable collection in the code behind in check box on Check event. Please Check check box event code).
I can see that first time when I load default data in the view model the line is hit by the break point in the "Getter" part of "SelectedFilterResources" . But I never get a hit in the Setter part of the property.
Custom Text Box
The custom text box has the "CaptionCollectionChanged" callback event. It is the place where I have all logic to achieve my scenario. "Resources item" here is a type of Model.
public class ResourceSelectionBoxLable : TextBox
{
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
IsReadOnly = true;
}
public static List<ResourceItem> LocalFilterdResources = new List<ResourceItem>();
#region Dependancy Properties
public static readonly DependencyProperty FilterdResourcesProperty =
DependencyProperty.Register("SelectedFilterdResources",
typeof (ObservableCollection<ResourceItem>),
typeof (ResourceSelectionBoxLable),
new PropertyMetadata(new ObservableCollection<ResourceItem>(),
CaptionCollectionChanged));
public ObservableCollection<ResourceItem> SelectedFilterdResources
{
get
{
return
(ObservableCollection<ResourceItem>) GetValue(FilterdResourcesProperty);
}
set
{
SetValue(FilterdResourcesProperty, value);
LocalFilterdResources = new List<ResourceItem>(SelectedFilterdResources);
}
}
#endregion
private static void CaptionCollectionChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var resourceSelectionBoxLable = d as ResourceSelectionBoxLable;
if (resourceSelectionBoxLable != null)
{
if (LocalFilterdResources.Count <= 0)
{
resourceSelectionBoxLable.Text = "Resources"
}
else
{
var actualwidthOflable = resourceSelectionBoxLable.ActualWidth;
var newValue = e.NewValue as string;
//Get the Wdith of the Text in Lable
TextBlock txtMeasure = new TextBlock();
txtMeasure.FontSize = resourceSelectionBoxLable.FontSize;
txtMeasure.Text = newValue;
double textwidth = txtMeasure.ActualWidth;
//True if Text reach the Limit
if (textwidth > actualwidthOflable)
{
var appendedString = string.Join(", ",
LocalFilterdResources.Select(item => item.ResourceCaption)
.ToArray());
resourceSelectionBoxLable.Text = appendedString;
}
else
{
if (LocalFilterdResources != null)
{
var morestring = string.Join(", ",
(LocalFilterdResources as IEnumerable<ResourceItem>).Select(item => item.ResourceCaption)
.ToArray());
var subsring = morestring.Substring(0, Convert.ToInt32(actualwidthOflable) - 4);
resourceSelectionBoxLable.Text = subsring + "...";
}
}
}
}
}
}
Custom Combo Box.
This is the control where I use the above custom label. This is also a custom control so most of the properties and styles in this controls are custom made. "DPItemSlectionBoxTemplate" is a dependency property where I enable the Selection Box of the combo box by adding an attached property to the control template. This control works fine, because I use this control in other places in my system for different purposes.
<styles:CommonMultiComboBox
x:Name="Resourcescmb" IsEnabled="{Binding IsResourceComboEnable,Mode=TwoWay}"
IsTabStop="False"
>
<styles:CommonMultiComboBox.ItemDataTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelect, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Click="CheckBox_Click"
Content="{Binding ResourceCaption}"
Style="{StaticResource CommonCheckBoxStyle}"
Tag ="{Binding}"
Checked="Resource_ToggleButton_OnChecked" />
</DataTemplate>
</styles:CommonMultiComboBox.ItemDataTemplate>
<styles:CommonMultiComboBox.DPItemSlectionBoxTemplate>
<DataTemplate>
<filtersTemplate:ResourceSelectionBoxLable
Padding="0"
Height="15"
FontSize="10"
SelectedFilterdResources="{Binding DataContext.FilterdResources,ElementName=root ,Mode=TwoWay}" />
</DataTemplate>
</styles:CommonMultiComboBox.DPItemSlectionBoxTemplate>
</styles:CommonMultiComboBox>
ViewModel
private ObservableCollection<ResourceItem> _resourceItems;
public ObservableCollection<ResourceItem> FilterdResources
{
get { return _resourceItems; }
set
{
SetOnChanged(value, ref _resourceItems, "FilterdResources");
}
}
Constructor of View Model
FilterdResources=new ObservableCollection<ResourceItem>();
"SetOnChanged" is a method in the View Model base class where we have the INotifyPropertichanged implementation.
Check Box Event
private void Resource_ToggleButton_OnChecked(object sender, RoutedEventArgs e)
{
var senderControl = sender as CheckBox;
if(senderControl==null)
return;
var selectedContent=senderControl.Tag as ResourceItem;
if (selectedContent != null)
{
ViewModel.FilterdResources.Add(selectedContent);
}
}
I can access the View Model from the Code behind through the View Model Property.
Why is the call back event not notified when I change bounded values? Am i missing something here? Dependency properties are supposed to work for two way bindings aren't they? Could any one please help me on this?
Thanks in advance.
Looks like your issue is that you're expecting the CaptionCollectionChanged event to fire when the bound collection is changed (i.e. items added or removed). When in fact this event will fire only when you're changing an instance of the bound object.
What you need to do here is to subscribe to ObservableCollection's CollectionChanged event in the setter or change callback (which you already have - CaptionCollectionChanged) of your dependency property.
public static readonly DependencyProperty FilterdResourcesProperty =
DependencyProperty.Register("SelectedFilterdResources",
typeof (ObservableCollection<ResourceItem>),
typeof (ResourceSelectionBoxLable),
new PropertyMetadata(new ObservableCollection<ResourceItem>(),
CaptionCollectionChanged));
private static void CaptionCollectionChanged(DependencyObject d,
DependencyPropertyChangedEventArgs args)
{
var collection = args.NewValue as INotifyCollectionChanged;
if (collection != null)
{
var sender = d as ResourceSelectionBoxLable;
if (sender != null)
{
collection.CollectionChanged += sender.BoundItems_CollectionChanged;
}
}
}
private void BoundItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// Do your control logic here.
}
Don't forget to add cleanup logic - unsubscribe from collection change when collection instance is changed and so on.

Winforms ToolTip get and change all assigned tooltips

I'm cycling over my WinForms controls and give their Text and ToolTipText to my Translation service for translation.
Example:
foreach (ToolStripItem item in toolStrip.Items)
{
if (item is ToolStripMenuItem)
{
item.ToolTipText = Translate(item.ToolTipText);
item.Text = Translate(item.Text);
}
}
However, I cannot access tooltips set by using the WinForms ToolTip control.
I see I can cycle over the components. Can I Get and Set their tooltips?
protected void TranslateToolTip(ToolTip toolTip)
{
foreach (var component in toolTip.Container.Components)
{
// Doesn't work. No ToolTipText property
component.ToolTipText = Translate(component.ToolTipText);
}
}
Can I access the tooltip text directly from the control?
To set a tooltip text on all components in your form like button1 etc. I think you should use something like this:
foreach (var control in this.Controls) {
ToolTip1.SetToolTip(this.control, "Your text");
}
That's because ToolTip doesn't have a Text property and it's set like on example above.
See also ToolTip Class and ToolTip.SetToolTip Method and ToolTip.GetToolTip Method
Or you can try something like that but not sure if this gonna work:
protected void TranslateToolTip(ToolTip toolTip)
{
foreach (var component in toolTip.Container.Components)
{
// Doesn't work. No ToolTipText property
// component.ToolTipText = Translate(component.ToolTipText);
toolTip.SetToolTip(component , (string)Translate(toolTip.GetToolTip(component));
}
}
I don't know what Translate(component.ToolTipText) is supposed to return. If it's just translated string, then we don't need the (string) part in my exmple.
Hope it helps.
EDIT: Fixed second example to show how to Set and Get tooltip text from specific control.

Dynamically adding rows to ASP.NET repeater in reaction to event

I am trying to create ASP.NET server control (pure code, without ascx template - because control must be completly contained in .dll and it must not rely on external .ascx files), and I have a problem with dynamically adding items to repeater.
I want to add item to repeater in reaction to SelectedIndexChanged event, but when i do second DataBind() in that event, i lose data from ViewModel (for example, textboxes contains default data instead of text entered by user).
Simplified version of my code (in large portion borrowed from MS composite control example - http://msdn.microsoft.com/en-us/library/3257x3ea%28v=vs.100%29.aspx):
[ToolboxData("<{0}:FilterControl runat=server />")]
public class FilterControl : CompositeControl, IPostBackDataHandler
{
private List<FilteringProperty> elements = new List<FilteringProperty>();
private DropDownList filteringElementsDropDownList;
private Repeater usedFiltersRepeater;
[Bindable(true), DefaultValue(null), Description("Active filters")]
public List<FilteringProperty> UsedElements
{
get
{
EnsureChildControls();
if (ViewState["UsedElements"] == null)
{
ViewState["UsedElements"] = new List<FilteringProperty>();
}
return (List<FilteringProperty>)ViewState["UsedElements"];
}
set
{
EnsureChildControls();
ViewState["UsedElements"] = value;
}
}
protected override void RecreateChildControls()
{
EnsureChildControls();
}
protected override void CreateChildControls()
{
Controls.Clear();
filteringElementsDropDownList = new DropDownList { AutoPostBack = true };
usedFiltersRepeater = new Repeater();
foreach (var element in elements)
{
filteringElementsDropDownList.Items.Add(new ListItem(element.DisplayName));
}
filteringElementsDropDownList.SelectedIndexChanged += (sender, e) =>
{
string selectedText = filteringElementsDropDownList.SelectedValue;
FilteringProperty condition = elements.First(x => x.DisplayName == selectedText);
var toRemove = filteringElementsDropDownList.Items.Cast<ListItem>().FirstOrDefault(x => x.Text == condition.DisplayName);
if (toRemove != null)
{
filteringElementsDropDownList.Items.Remove(toRemove);
}
UsedElements.Add(condition);
// ======> A <========
};
usedFiltersRepeater.ItemDataBound += (sender, args) =>
{
FilteringProperty dataItem = (FilteringProperty)args.Item.DataItem;
Control template = args.Item.Controls[0];
TextBox control = (TextBox)template.FindControl("conditionControl");
control.Text = dataItem.DisplayName;
// ======> C <========
};
usedFiltersRepeater.ItemTemplate = // item template
usedFiltersRepeater.DataSource = UsedElements;
usedFiltersRepeater.DataBind();
// ======> B <========
Controls.Add(filteringElementsDropDownList);
Controls.Add(usedFiltersRepeater);
}
}
I marked important portions of code with (A), (B) and (C)
The problem is, (A) is executed after DataBinding (B and C), so changes in UsedElements are not visible until next postback.
It is possible to add usedFiltersRepeater.DataBind(); after (A), but than all controls are recreated without data from viewstate (i.e empty)
Is there a way to dynamically change repeater after databinding, such that data of contained controls is preserved?
Tl;dr - i have a DropDownList and I want to add editable items to Repeater on SelectedIndexChanged (without losing viewstate).
I finally solved my problem.
My solution is rather dirty, but it seems to work fine.
Instead of simple databinding:
I get state from all controls in repeater and save it in temporary variable (state for each control includes everything, such as selected index for dropdownlists) using my function GetState()
modify this state in any way i want
restore full state using my function SetState()
For example:
FilterState state = GetState();
state.Conditions.Add(new ConditionState { Item = condition });
SetState(state);

CompositeDataBoundControl - databound values overwritten before event is fired due to DummyDataSource

I have a custom servercontrol that inherits from CompositeDataBoundControl. I have three templates: one header template, one footer template and one item template. The item template can contain a checkbox that I use to decide if I should delete the item.
In the footer and/or header templates I have a button with a CommandName of "DeleteItem". When that button is clicked, I handle the event in OnBubbleEvent:
if (cea.CommandName == "DeleteItem") {
//loop through the item list and get the selected rows
List<int> itemsToDelete = new List<int>();
foreach(Control c in this.Controls){
if (c is ItemData) {
ItemData oid = (ItemData)c;
CheckBox chkSel = (CheckBox)oid.FindControl("chkSelected");
if (chkSel.Checked) {
itemsToDelete.Add(oid.Item.Id);
}
}
}
foreach (int id in itemsToDelete) {
DeleteItem(id);
}
}
}
The problem is that Item is null since the CreateChildControls method already has been run as asp.net needs to recreate the control hierarchy before the event fire. It uses the DummyDataSource and a list of null objects to recreate the control hierarchy:
IEnumerator e = dataSource.GetEnumerator();
if (e != null) {
while (e.MoveNext()) {
ItemData container = new ItemData (e.Current as OrderItem);
ITemplate itemTemplate = this.ItemTemplate;
if (itemTemplate == null) {
itemTemplate = new DefaultItemTemplate();
}
itemTemplate.InstantiateIn(container);
Controls.Add(container);
if (dataBinding) {
container.DataBind();
}
counter++;
}
}
The problem is this line: ItemData container = new ItemData (e.Current as OrderItem); When the control hierarchy is rebuilt before the event is fired, the e.Current is null, so when I try to find out which item was marked for deletion, I get 0 since the original value has been overwritten.
Any suggestions on how to fix this?
I've finally found a solution that works. The problem is that the bound data is only connected to the control when being bound and directly after(normally accessed in a ItemDataBound event).
So to solve it I had to add a hidden literal containing the data item id to the container control. In the OnBubbleEvent I find the hidden literal and get the id:
ItemData oid = (ItemData)c;
CheckBox chkSel = (CheckBox)oid.FindControl("chkSelected");
if(chkSel != null) {
if(chkSel.Checked) {
Literal litId = (Literal)oid.FindControl("litId");
itemsToDelete.Add(Utils.GetIntegerOnly(litId.Text));
}
}

Categories