Given that every grid, combobox, checkboxlist and in general multi row control supports binding directly to any IEnumerable what is the point of the ObjectDataSource?
Why would one use it as opposed to binding directly to your collection? Particularly if you already have reasonable separation of concerns in your business, presentation and data layers?
I also feel this is an even more relevant question since the introduction of LINQ. I have often found that when binding I would like to perform some further ordering, exclusion and so forth using LINQ and I believe this is not possible when using the ObjectDataSource without creating a specific method for your (potentially single use case)?
So when is it appropriate to use an ObjectDataSource and what are the advantages compared to direct binding to IEnumerable?
First, ObjectDataSource is usually used in ASP.NET WebForms (aspx). ObjectDataSource is located in System.Web.UI.WebControls, as you can see this link on MSDN Library:
http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.objectdatasource.aspx
Using ObjectDataSource to bind your data means you bind you'll have datasource as an object, can be in form of DataSet or any other .NET object that implements IEnumerable. Using ObjectDataSource means you have to perform your own Select, Update, Insert and Delete method that usually found in SqlDataSource.
There's this nice walkthrough in MSDN Library: Walkthrough: Data Binding to a Custom Business Object
But binding to a simple IEnumerable without implementing IListSource (like DataTable has) means you won't have nice feature such as data bindings to a complex data control such as GridView. And you'll lose other feature too, because a simple IEnumerable alone can't be bound in two ways to other list control such as ListView and GridView.
To have your data to be bindable two way, your object must also implement INotifyPropertyChanged interface before added into the IListSource as data item.
Samples:
public class Employee : BusinessObjectBase
{
private string _id;
private string _name;
private Decimal parkingId;
public Employee() : this(string.Empty, 0) {}
public Employee(string name) : this(name, 0) {}
public Employee(string name, Decimal parkingId) : base()
{
this._id = System.Guid.NewGuid().ToString();
// Set values
this.Name = name;
this.ParkingID = parkingId;
}
public string ID
{
get { return _id; }
}
const string NAME = "Name";
public string Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
// Raise the PropertyChanged event.
OnPropertyChanged(NAME);
}
}
}
const string PARKING_ID = "Salary";
public Decimal ParkingID
{
get { return parkingId; }
set
{
if (parkingId != value)
{
parkingId = value;
// Raise the PropertyChanged event.
OnPropertyChanged(PARKING_ID);
}
}
}
}
This is the implementation of INotifyPropertyChanged:
public class BusinessObjectBase : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
private void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (null != PropertyChanged)
{
PropertyChanged(this, e);
}
}
#endregion
}
You can bind to an enum with it if you dont want codebehind.
Related
All ASP.NET data bound controls have an additional event in ASP.NET 4.5, CreatingModelDataSource. It provides a great way to create our own implementation of ModelDataSource and bind to a control like GridView.
I created a sample ModelDataSource and hooked it up with a GridView in the CreatingModelDataSource event as follows:
protected void gvStudent_CreatingModelDataSource(object sender, CreatingModelDataSourceEventArgs e)
{
e.ModelDataSource = new MyModelDataSource((GridView)sender);
}
Following is the sample ModeldataSource implementation that I wrote:
public class MyModelDataSource : ModelDataSource
{
MyDataSourceView view;
public MyModelDataSource(Control control)
: base(control)
{
}
public override ModelDataSourceView View
{
get
{
if (view == null)
{
view = new MyDataSourceView(this);
}
return view;
}
}
}
And following is MyModelDataSourceView:
public class MyDataSourceView : ModelDataSourceView
{
private MyModelDataSource _owner;
private StudentRepository repository;
public MyDataSourceView(MyModelDataSource owner)
: base(owner)
{
_owner = owner;
repository = new StudentRepository();
}
protected override IEnumerable ExecuteSelect(DataSourceSelectArguments arguments)
{
return repository.GetAll();
}
}
But the ExecuteSelect method doesn't get called automatically. It gets called when I set a SelectMethod to the GridView. As the result of ExecuteSelect method is finally bound to the GridView, specifying the SelectMethod doesn't make any sense here. Also, the event is not fired when gridView.DataBind() is called. Is there any way to kick off the things and make this event called automatically?
CreatingModelDataSource() was put in for the Model data source which is used in Model Binding. When you use model binding you need to specify the Select() method for model binding to work with GridVIew
I'm trying to figure out how to create a web server control which is basically an ExpandoObject.
The desire is to automatically create a property on the control when it is created in the aspx markup.
For example:
<x:ExpandoControl someProperty="a value"></x:ExpandoControl>
Where the someProperty attribute does not yet exist as a property on the control.
I should also mention that I don't strictly need any functionality of Control or WebControl. I just need to be able to declare it in markup with runat="server" (which in and of itself may require it to be a control, at least that's what I'm thinking).
Is it possible? If so how can I get started?
Many thanks.
I think your first bet would be to implement IAttributeAccessor:
public interface IAttributeAccessor
{
string GetAttribute(string key);
void SetAttribute(string key, string value);
}
The ASP.NET page parser calls IAttributeAccessor.SetAttribute for each attribute it cannot map to a public property.
So perhaps you can start with
public class ExpandoControl : Control, IAttributeAccessor
{
IDictionary<string, object> _expando = new ExpandoObject();
public dynamic Expando
{
{
return _expando;
}
}
void IAttributeAccessor.SetValue(string key, string value)
{
_expando[key] = value;
}
string IAttributeAccessor.GetValue(string key)
{
object value;
if (_expando.TryGetValue(key, out value) && value != null)
return value.ToString();
else
return null;
}
}
Im trying to make a get and set to make my List<> persistent. I think my intention is clear with the code. I want the List to remain throughout postbacks so i dont loose data in it.
What am i doing wrong? Thanks to my AlertPopUp i can see that the Get is triggered, but never the Set. So the List comes back as containint zero items when it should have several items.
private List<string> accountIDsSelectedForDeletion = new List<string>();
public List<string> AccountIDsSelectedForDeletion
{
get {
if (ViewState["AccountIDsSelectedForDeletion"]!= null)
{
accountIDsSelectedForDeletion = ViewState["AccountIDsSelectedForDeletion"] as List<string>;
AlertPopUp.QuickDebugMessage("getting list from viewstate. Count: "+ accountIDsSelectedForDeletion.Count);
}
AlertPopUp.QuickDebugMessage("returning list");
return accountIDsSelectedForDeletion;
}
set {
AlertPopUp.QuickDebugMessage("setting list to viewstate. Count: " + accountIDsSelectedForDeletion.Count);
accountIDsSelectedForDeletion = value;
ViewState["AccountIDsSelectedForDeletion"] = accountIDsSelectedForDeletion;
}
Could it be because i change the List by its Add() method?
In a nutshell, yes. .Add() is mutating the list, while the Set property acts on the list as a set piece.
To accomplish what your trying to do here, I would create an inherited type from List, so that I could inject code into the Add Remove (ext, there are more) methods. Like:
//not syntatically correct sorry
public class MyList : List<string>
{
public override Add(string NewItem)
{
//do a extra peristance step here
//
me.Add(NewItem);
}
}
Or have a class that wraps a private List<> and manages the Add/Remove methods, maybe like:
public class MyList
{
private List<string> listItems { get; set; }
//don't expose the list in a mutable state, IEnumerable instead
public IEnumerable<string> Items { get { return this.listItems; } }
public void Add(string NewItem)
{
this.ListItems.Add(NewItem);
}
}
Then just replace List with this type on your page level class.
By the way, persisting to the ViewState bloats the amount of information you have to send up and down the wire per page load. Have you considered Session, or the Cache object?
I'm completely new to CM and also to learn it I'm migrating an application from MVVM light to Caliburn Micro. In my original code, I had a VM which responds to some UI actions (via commands) to replace some text into a string. The position is given by the view, using the textbox selection.
So the VM has (1) a bound string property representing the textbox's text, (2) another bound string property to represent the new text to be added, and (3) needs to know selection start and length in order to replace the right portion of text with the new one.
In my original code, I had a custom DialogMessage-derived object sent in the VM command implementation with a couple of properties for selection data: when the command was issued, the message was sent, and the view received it and filled it with its textbox selection start and length; then the VM was called back and could use these data.
Which would be the best way of implementing this in CM? I'd prefer the VM to remain agnostic of the view, so I don't like too much the idea of accessing the view from it. I'd rather opt for a "message"-based mechanism like the above, but I'm not sure how I can implement it in CM: I would probably look at IResult, but most of the samples I find are related to coroutines and I'm not sure how to relate the void ReplaceText() method of the VM to the view code behind.
Could anyone point me in the right direction, and/or to some code samples about dialog-like interactions between VM 'command' methods and view? Thanks!
I'd probably look at the IResult option. You'll have access to the view so code that you would have had in the code behind can be in your Result and not in your VM.
Here is code from a ShowDialog result. I believe I grabbed it from the CM discussion group. Search the discussion group for ShowDialog for more examples. The GameLibrary sample that comes with CM also has some.
public class ShowDialog : IResult
{
private readonly Type _screenType;
private readonly string _name;
[Import]
public IWindowManager WindowManager { get; set; }
public ShowDialog(string name)
{
_name = name;
}
public ShowDialog(Type screenType)
{
_screenType = screenType;
}
public void Execute(ActionExecutionContext context)
{
var screen = !string.IsNullOrEmpty(_name)
? IoC.Get<object>(_name)
: IoC.GetInstance(_screenType, null);
Dialog = screen;
WindowManager.ShowDialog(screen);
var deactivated = screen as IDeactivate;
if (deactivated == null)
Completed(this, new ResultCompletionEventArgs());
else
{
deactivated.Deactivated += (o, e) =>
{
if (e.WasClosed)
{
Completed(this, new ResultCompletionEventArgs());
}
};
}
}
public object Dialog { get; private set; }
public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };
public static ShowDialog Of<T>()
{
return new ShowDialog(typeof (T));
}
}
edit: If you extend TextBox you can bind SelectedText.
public class TextBoxEx : TextBox
{
public static readonly DependencyProperty SelectedTextProperty = DependencyProperty.Register("SelectedText", typeof(string), typeof(TextBoxEx), new PropertyMetadata("oa"));
public TextBoxEx()
{
SelectionChanged += UpdateDependencyProperty;
}
private void UpdateDependencyProperty(object sender, RoutedEventArgs e)
{
SelectedText = base.SelectedText;
}
public new string SelectedText
{
get { return GetValue(SelectedTextProperty).ToString(); }
set { SetValue(SelectedTextProperty, base.SelectedText); }
}
}
then:
<SLTest:TextBoxEx x:Name="MyTextBox2"
Grid.Row="1"
Width="200"
SelectedText="{Binding SelectedText, Mode=TwoWay}"
Text="This is some text." />
Is it possible to define a control to have non-specified set of attributes? For instance:
<MyPrefix:MyControl SomeAttribute="SomeValue" runat="server"/>
I don't want to define a property on the control class beforehand for "SomeAttribute." I'd really just like a HashTable or some other construct like this:
"SomeAttribute" => "SomeValue"
So this control can be used in many places with attributes that are essentially made up at runtime.
I'm wonder if there's some parsing method I can override which iterates through the attributes at parse time. I can:
Look for a property with the name and set it
If I don't find such a property, put the attribute name and value into a HashTable
Possible?
You want to use the IAttributeAccessor interface.
Defines methods used by ASP.NET server controls to provide programmatic access to any attribute declared in the opening tag of a server control.
Example control:
using System;
using System.Collections.Generic;
using System.Web.UI;
namespace App_Code.Controls {
public class OutputAttributesControl : Control, IAttributeAccessor {
private readonly IDictionary<String, String> _attributes = new Dictionary<String, String>();
protected override void Render(HtmlTextWriter writer) {
writer.Write("Attributes:<br/>");
if (_attributes.Count > 0) {
foreach (var pair in _attributes) {
writer.Write("{0} = {1} <br/>", pair.Key, pair.Value);
}
} else {
writer.Write("(None)");
}
}
public String GetAttribute(String key) {
return _attributes[key];
}
public void SetAttribute(String key, String value) {
_attributes[key] = value;
}
}
}
Invocation:
<AppCode:OutputAttributesControl runat="server" attr="value" />
Output:
Attributes:
attr = value
Caveats:
It seems that SetAttribute is only called on attributes that can not be resolved normally. This means you'll not see the id- or the runat-attribute in your code. Assigned properties (attr="<%= DateTime.Now %>") show up as an empty string. Databound properties does not show up at all in design mode, but works in normal mode (assuming that someone called DataBind, as usual).