I have an ASP.NET page with a FormView data-bound to an ObjectDataSource that uses dynamically generated templates to render the UI based on layout information from the application's database. I've been able to get the templates to render correctly and everything seems fine until I click one of the buttons to change modes - nothing changes.
My code is based on the explanations provided in the following articles/posts:
http://www.codeproject.com/KB/aspnet/DynamicFormview.aspx
http://msdn.microsoft.com/en-us/library/ms227423.aspx
http://msdn.microsoft.com/en-us/library/y0h809ak(vs.71).aspx
http://forums.asp.net/p/1379371/2911424.aspx#2911424?Data+Binding+with+Dynamically+Created+controls+not+working+both+ways
In a nutshell, in the Page.OnInit method I assign an instance of my templates to the FormView EditItemTemplate, EmptyDataTemplate, InsertItemTemplate and ItemTemplate properties (a different instance for each property with the appropriate controls, layout, etc for that template). I see that the InstantiateIn method of the template corresponding to the default mode is called, the control hierarchy is created correctly and the UI rendered as expected.
I have a set of button controls in each of my templates that enable the mode switches. So, for instance, in the ItemTemplate, I have a button with CommandName="New". I expect that clicking this button will cause the FormView to change into the Insert mode. Instead, I get the postback and InstantiateIn is called on my ItemTemplate. The handlers I've attached to the FormView's ModeChanging and ModeChanged events do not fire.
When I step through the control hierarchy, I see the same object model as the page I created in markup - with one exception. I am using the HtmlTable, HtmlTableRow and HtmlTableCell controls to construct the layout whereas the markup uses <table>, <tr> and <td> elements.
Any thoughts on what I'm missing? I'd really like to get this working with the automatic binding (through event bubbling) to change modes rather than have to manually create and code the buttons and their actions.
Here is the code used to generate the template:
public class FormViewTemplate : INamingContainer, ITemplate
{
private Boolean _childControlsCreated;
private Panel _panel;
public FormViewTemplate(TemplateMode mode) { Mode = mode; }
public TemplateMode Mode { get; private set; }
private void CreateChildControls()
{
_panel = new Panel();
_panel.Controls.Add(CreateButtons());
switch (Mode)
{
case TemplateMode.Edit:
_panel.Controls.Add(new LiteralControl("Edit Mode"));
break;
case TemplateMode.Empty:
_panel.Controls.Add(new LiteralControl("Empty Mode"));
break;
case TemplateMode.Insert:
_panel.Controls.Add(new LiteralControl("Insert Mode"));
break;
case TemplateMode.ReadOnly:
_panel.Controls.Add(new LiteralControl("Read-Only Mode"));
break;
}
}
private Panel CreateButtons()
{
var panel = new Panel();
var table = new HtmlTable()
{
Border = 0,
CellPadding = 2,
CellSpacing = 0
};
panel.Controls.Add(table);
var tr = new HtmlTableRow();
table.Rows.Add(tr);
var td = new HtmlTableCell();
tr.Cells.Add(td);
var addButton = new ASPxButton()
{
CommandName = "New",
Enabled = (Mode == TemplateMode.ReadOnly),
ID = "AddButton",
Text = "Add"
};
td.Controls.Add(addButton);
return panel;
}
private void EnsureChildControls()
{
if (!_childControlsCreated)
{
CreateChildControls();
_childControlsCreated = true;
}
}
void ITemplate.InstantiateIn(Control container)
{
EnsureChildControls();
container.Controls.Add(_panel);
}
}
(Note that the template is cached so the control hierarchy is only built once.)
Related
I am seeing something strange where an instance of ContentView from a ControlTemplate affects how another instance of the ContentView looks, under the same page, from another ControlTemplate.
ContentView BLA:
ScrollView
L Accordion control
L Accordion Item
Control template A:
BLA
Control template B:
Tab control
L Tab 1 Content: BLA
L Tab 2 Content: other
Somehow switching ControlTemplate from A to B, B to A,
And the height of the accordion item remain from the first template.
It is not the same instance.
How can I make sure the native controls are disposed of when switching ControlTemplate?
Normally, we use DataTemplateSelector to switch templates.
For the Limitations in the MSdocs, the warnings, however the reason behind it, is due to the caching of templates in the renderer. If you change it up, the previous template will be cached in the renderer and there will be no longer any way to reference or dispose of it, until the ListView is disposed of. Hence as you scroll, it will continue to cache new templates and never free up the old ones.
Hope this would be helpful to you.
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/templates/data-templates/selector
I moved away from template switching as the template is being cached, meaning the last view is still in memory, and this causes all kind of problems such as binding errors and layout measures turning out wrong.
I now use MyView which inherits ContentView:
public class MyView : ContentView
{
public static readonly BindableProperty DataProperty =
BindableProperty.Create(nameof(Data), typeof(object), typeof(MyView ), null, propertyChanging: OnDataPropertyChanging);
public object Data
{
get => (object)GetValue(DataProperty);
set => SetValue(DataProperty, value);
}
private static void OnDataPropertyChanging(BindableObject bindable, object oldValue, object newValue)
{
var instance = bindable as MyViewModel;
instance.Content = null;
if (newValue == null)
{
return;
}
var type = newValue.GetType();
View view = null;
if (type == typeof(MyViewModel1))
{
view = new BLA();
}
else if (type == typeof(MyViewModel2))
{
view = new other();
}
if (view != null)
{
view.BindingContext = newValue;
instance.Content = view;
}
}
}
I am new to MVVM and Caliburn.Micro.
I am trying to do a simple application using Caliburn.Micro that has a combobox.
The combobox items are added dynamically when the program is running, the problem is that the combobox stops updating its content after the first item navigation.
In View:
The combobox is declared as follows:
<ComboBox x:Name="cmbProductList" />
In ViewModel:
I declare the following list for combobox items:
List<string> L = new List<string> { };
I am using the following method to fill combobox item:
public List<string> cmbProductList
{
get
{
return L;
}
set
{
L = value;
NotifyOfPropertyChange("cmbProductList");
}
}
And this command to add an item to this list:
L.Add(p1.ID.ToString());
The container you are using for your combobox items is not the proper one. List has no way of notifying the UI of its changes (items added or removed). Instead you need to use an ObservableCollection. Or even better, you can use BindableCollection that is Caliburn.Micro's customized version of ObservableCollection:
BindableCollection is a simple collection that inherits from ObservableCollection, but that ensures that all its events are raised on the UI thread as well.
http://caliburnmicro.com/documentation/introduction
Your property would look like this:
private BindableCollection<string> _cmbProductList;
public BindableCollection<string> cmbProductList
{
get
{
return _cmbProductList;
}
set
{
_cmbProductList = value;
NotifyOfPropertyChange("cmbProductList");
}
}
and of course its initialization would look like this:
_cmbProductList = new BindableCollection<string>();
The rest can stay unchanged.
I have a requirement in my program that the object bound (from ViewModel) in a Combobox is updated as soon as an item is selected in the combobox. Currently, the object only updates once the edit is committed by either pressing Enter or leaving the cell. The user does not want the extra step.
My thought would be to have the act of selecting an item in the combobox trigger the CommitEdit() method and then CancelEdit(). However, I cannot seem to find a way to hook into the SelectionChanged event for the DataGridComboBoxColumn as it is not available.
Other suggestions have been to listen in the viewmodel for a property change event but the property is not changed until the Cell Edit is finished.
Can anyone think of a way to cause the selection of a new item (index) in a DataGridCombobox to close the edit of the cell as if the user pressed Enter or left the cell?
NOTE: I cannot use .NET 4.5 due to customer limitations.
I've had similar issue but i just found out the solution using attached property, This may not exactly fix your problem but it will help in datagrid selection changed issue.
Below is the attached property and handler methods
public static readonly DependencyProperty ComboBoxSelectionChangedProperty = DependencyProperty.RegisterAttached("ComboBoxSelectionChangedCommand",
typeof(ICommand),
typeof(SpDataGrid),
new PropertyMetadata(new PropertyChangedCallback(AttachOrRemoveDataGridEvent)));
public static void AttachOrRemoveDataGridEvent(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
DataGrid dataGrid = obj as DataGrid;
if (dataGrid != null)
{
if (args.Property == ComboBoxSelectionChangedProperty)
{
dataGrid.SelectionChanged += OnComboBoxSelectionChanged;
}
}
else if (args.OldValue != null && args.NewValue == null)
{ if (args.Property == ComboBoxSelectionChangedProperty)
{
dataGrid.SelectionChanged -= OnComboBoxSelectionChanged;
}
}
}
private static void OnComboBoxSelectionChanged(object sender, SelectionChangedEventArgs args)
{
DependencyObject obj = sender as DependencyObject;
ICommand cmd = (ICommand)obj.GetValue(ComboBoxSelectionChangedProperty);
DataGrid grid = sender as DataGrid;
if (args.OriginalSource is ComboBox)
{
if (grid.CurrentCell.Item != DependencyProperty.UnsetValue)
{
//grid.CommitEdit(DataGridEditingUnit.Row, true);
ExecuteCommand(cmd, grid.CurrentCell.Item);
}
}
}
SpDataGrid is the custom control that i inherited from data grid.
I added below style in generic.xaml as i use the resourcedictionary for style (you can certainly add inside the datagrid).
<Style TargetType="{x:Type Custom:SpDataGrid}">
<Setter Property="Custom:SpDataGrid.ComboBoxSelectionChangedCommand" Value="{Binding ComboBoxSelectionChanged}"/>
</Style>
ComboBoxSelectionChanged is the command in my viewmodel. OnComboBoxSelectionChanged i commented the commitedit because in my case the values were already updated.
Let me know if anything is not clear or any questions. Hope this helps.
I want to display a drop down menu when a user clicks a button. Something like comboBox but instead of the comboBox its a button. How can I do it??
I solved it using PopupMenu. Here is the code for other's reference.
public static Rect GetElementRect(FrameworkElement element)
{
GeneralTransform buttonTransform = element.TransformToVisual(null);
Point point = buttonTransform.TransformPoint(new Point());
return new Rect(point, new Size(element.ActualWidth, element.ActualHeight));
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
var menu = new PopupMenu();
menu.Commands.Add(new UICommand("Label", (command) =>
{
//do work
}));
// We don't want to obscure content, so pass in a rectangle representing the sender of the context menu event.
// We registered command callbacks; no need to handle the menu completion event
var chosenCommand = await menu.ShowForSelectionAsync(GetElementRect((FrameworkElement)sender));
if (chosenCommand == null) // The command is null if no command was invoked.
{
}
}
Milan,
You'll need to create a custom control or a user control that combines a button and a popup. You could also just implement this in-place with a button and popup. I suggest you look at Callisto's Menu control and start from there to implement your dropdown menu:
Callisto controls (includes a Menu)
I´m writing on a webpart for sharepoint, so I have to generate a Datagrid problematically.
The Situation is that I get a Dataview, generate the Gris and bind the Data.
One column should show a Image, so I have to generate a template column with item template.
So code looks like this:
//Instantiate the DataGrid, and set the DataSource
_grdResults = new DataGrid();
_grdResults.AutoGenerateColumns = false;
_grdResults.DataSource = view;
TemplateColumn colPic = new TemplateColumn();
colPic.HeaderText = "Image";
I found dozens of example for asp to create the item-template, but how construct one in code and bind it´s ImageUrl to "imgURL" of the Dataview?
thanks for any advice
Ren
You need to create a class that implements that ITemplate interface.
public class TemplateImplementation : ITemplate
{
public void InstantiateIn(Control container)
{
Image image = new Image();
image.DataBinding += Image_DataBinding;
container.Controls.Add(image);
}
void Image_DataBinding(object sender, EventArgs e)
{
Image image = (Image)sender;
object dataItem = DataBinder.GetDataItem(image.NamingContainer);
// If the url is a property of the data item, you can use this syntax
//image.ImageUrl = (string)DataBinder.Eval(dataItem, "ThePropertyName");
// If the url is the data item then you can use this syntax
image.ImageUrl = (string)dataItem;
}
}
You then set your ItemTemplate to an instance of this class.
colPic.ItemTemplate = new TemplateImplementation();