Shuffle DevExpress GridControl DataSource - data-binding

I need to shuffle the GridControl's DataSource. I use this property in a UserControl:
private List<Song> _songsDataSource;
public List<Song> SongsDataSource
{
get { return _songsDataSource; }
set
{
_songsDataSource = value;
if (!value.IsNull())
{
SongsBindingList = new BindingList<Song>(value);
songsBinding.DataSource = SongsBindingList;
}
}
}
Then i use a method that i clone, shuffle and append to the SongsDataSource property:
List<Song> newList = HelpClasses.Shuffle((List<Song>) SongsDataSource.Clone());
SongsDataSource = newList;
public static List<Song> Shuffle(List<Song> source)
{
for (int i = source.Count - 1; i > 0; i--)
{
int n = rng.Next(i + 1);
Song tmp = source[n];
source[n] = source[i - 1];
source[i - 1] = tmp;
}
return source;
}
Strange thing is that it doesn't seem to reflect the changes to the GridControl even i use the GridControl.RefreshDataSource() method after set the SongsDataSource method. If i check the DataSource order, shuffle was happened successfully.
Thank you.

Since you've changed the object originally set as a DataSource, calling RefreshDataSource() won't do any good cause you can't refresh something that's no longer there. Your problem is here:
List<Song> newList = HelpClasses.Shuffle((List<Song>) SongsDataSource.Clone());
SongsDataSource = newList; // the reference has changed, the grid doesn't know what to do when RefreshDataSource() is called.
You can pass the list as it is, without the need of cloning it. Also surround the Shuffle() method call with gridControl.BeginUpdate() end gridControl.EndUpdate() to prevent any updates to the grid while the elements of the DataSource are changing.

I had such problems with DevExpress GridControl. I think, that this situation caused by GridView(http://documentation.devexpress.com/#WindowsForms/clsDevExpressXtraGridViewsGridGridViewtopic), which creates automaticly for each GridControl.
This is part of GridControl responsible for visualization of DataSource.
If you need to change DataSource try:
GridView.Columns.Clear();
GridControl.DataSource = You_New_DataSource;
GridView.RefreshData();
GridControl.RefreshDataSource();

Related

how to update Visual Studio UI when using DynamicItemStart inside a vsix package

I'm implementing a DynamicItemStart button inside a Menu Controller. I'm loading the dynamic items for this button when Visual Studio starts. Everything is loaded correctly so the initialize method is called an I see all the new items in this Dynamic button. After the package is completely loaded I want to add more items to this Dynamic button, but since the package is already loaded the initialize method is not called again and I cannot see the new items in this Dynamic button. I only see the ones that were loaded when VS started.
Is there any way that I can force the update of this Dynamic button so it shows the new items?. I want to be able to update the VS UI after I added more items but outside the Initialize method.
The implementation I did is very similar to the one showed on this msdn example:
http://msdn.microsoft.com/en-us/library/bb166492.aspx
Does anyone know if an Update of the UI can be done by demand?
Any hints are greatly appreciated.
I finally got this working. The main thing is the implementation of a derived class of OleMenuCommand that implements a new constructor with a Predicate. This predicate is used to check if a new command is a match within the DynamicItemStart button.
public class DynamicItemMenuCommand : OleMenuCommand
{
private Predicate<int> matches;
public DynamicItemMenuCommand(CommandID rootId, Predicate<int> matches, EventHandler invokeHandler, EventHandler beforeQueryStatusHandler)
: base(invokeHandler, null, beforeQueryStatusHandler, rootId)
{
if (matches == null)
{
throw new ArgumentNullException("Matches predicate cannot be null.");
}
this.matches = matches;
}
public override bool DynamicItemMatch(int cmdId)
{
if (this.matches(cmdId))
{
this.MatchedCommandId = cmdId;
return true;
}
this.MatchedCommandId = 0;
return false;
}
}
The above class should be used when adding the commands on execution time. Here's the code that creates the commands
public class ListMenu
{
private int _baselistID = (int)PkgCmdIDList.cmdidMRUList;
private List<IVsDataExplorerConnection> _connectionsList;
public ListMenu(ref OleMenuCommandService mcs)
{
InitMRUMenu(ref mcs);
}
internal void InitMRUMenu(ref OleMenuCommandService mcs)
{
if (mcs != null)
{
//_baselistID has the guid value of the DynamicStartItem
CommandID dynamicItemRootId = new CommandID(GuidList.guidIDEToolbarCmdSet, _baselistID);
DynamicItemMenuCommand dynamicMenuCommand = new DynamicItemMenuCommand(dynamicItemRootId, isValidDynamicItem, OnInvokedDynamicItem, OnBeforeQueryStatusDynamicItem);
mcs.AddCommand(dynamicMenuCommand);
}
}
private bool IsValidDynamicItem(int commandId)
{
return ((commandId - _baselistID) < connectionsCount); // here is the place to put the criteria to add a new command to the dynamic button
}
private void OnInvokedDynamicItem(object sender, EventArgs args)
{
DynamicItemMenuCommand invokedCommand = (DynamicItemMenuCommand)sender;
if (null != invokedCommand)
{
.....
}
}
private void OnBeforeQueryStatusDynamicItem(object sender, EventArgs args)
{
DynamicItemMenuCommand matchedCommand = (DynamicItemMenuCommand)sender;
bool isRootItem = (matchedCommand.MatchedCommandId == 0);
matchedCommand.Enabled = true;
matchedCommand.Visible = true;
int indexForDisplay = (isRootItem ? 0 : (matchedCommand.MatchedCommandId - _baselistID));
matchedCommand.Text = "Text for the command";
matchedCommand.MatchedCommandId = 0;
}
}
I had to review a lot of documentation since it was not very clear how the commands can be added on execution time. So I hope this save some time whoever has to implement anything similar.
The missing piece for me was figuring out how to control the addition of new items.
It took me some time to figure out that the matches predicate (the IsValidDynamicItem method in the sample) controls how many items get added - as long as it returns true, the OnBeforeQueryStatusDynamicItem gets invoked and can set the details (Enabled/Visible/Checked/Text etc.) of the match to be added to the menu.

How to data bind Entity Framework objects to a FormView

I am data binding to many FormView controls using EF entity instances, but I have to resort to this ridiculous kludge in order to achieve what I want without using EntityDataSource controls:
propertyHeaderSection.DataSource = new List<PropertyDetailsModel> { _propertyDetails };
I suspect I will have to derive my own control from FormView and enable it to accept an almost POCO as a data source. Where do I start?
This is my implementation, sort of the same idea as patmortech, but i also found out that the ValidateDataSource method on the BaseDataBoundControl is what throws the exception at run-time if your datasource isn't enumerable.
public class CustomFormView : System.Web.UI.WebControls.FormView
{
public override object DataSource
{
get
{
if (!(base.DataSource is IEnumerable))
return new[] {base.DataSource};
return base.DataSource;
}
set
{
base.DataSource = value;
}
}
// This method complains at run time, if the datasource is not
// IListSource, IDataSource or IEnumerbale
protected override void ValidateDataSource(object dataSource)
{
//base.ValidateDataSource(dataSource);
}
}
EDIT:
Considering the suggestion, i've made some changes to the way i check if the assigned DataSource is enumerable or not. I have also managed to create a sample app (VS 2010 Solution) to demo the changes. The app can be downloaded from http://raghurana.com/blog/wp-content/attachments/FormViewDataProblem.zip
In short this is what i am checking to ensure that the existing datasource can be enumerated already or not:
public static bool CanEnumerate( this object obj )
{
if (obj == null) return false;
Type t = obj.GetType();
return t.IsArray ||
t.Implements(typeof (IEnumerable).FullName) ||
t.Implements(typeof (IListSource).FullName) ||
t.Implements(typeof (IDataSource).FullName);
}
Please feel free to suggest more changes, if this isnt quite the desired functionality. Cheers.
Not sure it's the best idea in the world, but this is how you could derive from FormView to allow single object data source values. It basically does the same check that the ValidateDataSource does internally, and then creates a list wrapper for the item if it's not already a valid type.
public class SingleObjectFormView : System.Web.UI.WebControls.FormView
{
public override object DataSource
{
get
{
return base.DataSource;
}
set
{
//will check if it's an expected list type, and if not,
//will put it into a list
if (! (value == null || value is System.Collections.IEnumerable || value is System.ComponentModel.IListSource || value is System.Web.UI.IDataSource) )
{
value = new List<object> { value };
}
base.DataSource = value;
}
}
}

ASP.Net Custom Control

I am developing a custom control that needs to display a dropdownlist as a composite control.
The drop down list gets populated from a Rest web service. The problem I am facing is that the dropdownlist only has DataTextField and DataValueField but I need a way of storing more values in the control i.e. I have a couple of other properties I need to access for the selected item.
What is the best way of going about this?
Here is the code I have so far:
[ValidationProperty("SelectedValue")]
public class SelectSurveyControl : Panel
{
private DropDownList ddlSurveys;
public string SelectedSurveyId
{
get
{
return SelectedValue;
}
}
public string SelectedSurveyJavascriptEmbedCode
{
get
{
return this.ddlSurveys.SelectedItem.Attributes[""];
}
}
public string SelectedValue
{
get
{
return ddlSurveys.SelectedValue;
}
set
{
if (ddlSurveys == null)
{
ddlSurveys = new DropDownList();
}
ddlSurveys.SelectedValue = value;
}
}
protected override void OnLoad(EventArgs e)
{
base.OnInit(e);
if (ddlSurveys == null)
{
ddlSurveys = new DropDownList();
}
IList<Survey> surveys = GetSurveys();
this.ddlSurveys.DataSource = surveys;
this.ddlSurveys.DataTextField = "title";
this.ddlSurveys.DataValueField = "id";
this.ddlSurveys.DataBind();
ddlSurveys.SelectedValue = this.SelectedValue;
ddlSurveys.CssClass = "umbEditorTextFieldMultiple charlimit";
ddlSurveys.Attributes.Add("SurveyId", SelectedSurveyId);
ddlSurveys.Attributes.Add("JavascriptEmbedingCode", SelectedSurveyId);
this.Controls.Add(ddlSurveys);
}
public IList<Survey> GetSurveys()
{
...
}
}
Try using a string join/split to store and retrieve the various values, then you don't have to customize your dropdown list very much.
For Example:
Text: Some Title
Value: 1|testing test|2/12/2010
This will let you store as many values as you want, so long as you choose an appropriate character to join and split on. I usually use the bar, as in my example above.
Side Note: I was looking at your selected value set handler and it needs some tweaking. You shouldn't check for a null drop down list, instead you should call EnsureChildControls() before each get and set instead. Make sure you override the CreateChildControls() method and create your controls there.
You could use a hidden field and iterate thru a copy of the returned Surveys like this:
foreach(Survey s in Surveys){
string val = s.id + ":" + s.<property1> + ":" + s.<property2>;
hiddenField.Value += val +",";
}
When you need to read from the hidden field, you use String.Split to separate the values into arrays using ',' as the separator and in each array, you split again using ':'.
In the first split Array1[0] who be the survey id and Array1[n!=0] would be the properties of the Survey with the id = Array1[0]. Array[n!=0] would then be split into Array2.
I would suggest handling empty property values with an empty string or something or else you might end up with unequal lengths especially if you specify StringSplitOptions.RemoveEmptyEntries.
Agricfowl

Flex: Database driven DataGrid: arrows disappearing

In Flex I'm using the following code to allow sorting in a DataGrid (the data is paged and sorted serverside).
private function headerReleaseHandler(event:DataGridEvent):void
{
var column:DataGridColumn = DataGridColumn(event.currentTarget.columns[event.columnIndex]);
if(this.count>0)
{
if(this.query.SortField == column.dataField)
{
this.query.SortAscending = !this.query.SortAscending;
}
else
{
this.query.SortField = column.dataField;
this.query.SortAscending = true;
}
this.fill();
}
event.preventDefault();
}
This works perfectly, except that the arrows that indicate sorting isn't shown. How can I accomplish that?
Thanks!
/Niels
There is an example here if this is what you are looking for:
http://blog.flexexamples.com/2008/02/28/displaying-the-sort-arrow-in-a-flex-datagrid-control-without-having-to-click-a-column/
It looks like you need to refresh the collection used by your dataprovider.
I have encountered the same problem and the only solution I found was to override the DataGrid and create a custom one.
Here is the class:
public class DataGridCustomSort extends DataGrid
{
public function DataGridCustomSort()
{
super();
addEventListener(DataGridEvent.HEADER_RELEASE,
headerReleaseHandlerCustomSort,
false, EventPriority.DEFAULT_HANDLER);
}
public function headerReleaseHandlerCustomSort(event:DataGridEvent):void {
mx_internal::sortIndex = event.columnIndex;
if (mx_internal::sortDirection == null || mx_internal::sortDirection == "DESC")
mx_internal::sortDirection = "ASC";
else
mx_internal::sortDirection = "DESC";
placeSortArrow();
}
}
You have to specifically call the placeSortArrow() method when you get the HEADER_RELEASE event and set the column index and direction information.
in the above code what does "this" refer to is it the datagrid because I am confused by this.query.SortField , I am assuming 'this' and "query' are your own custom objects. and why are you checking for count. what count is that.
Regards
-Mohan

ASP.NET TreeView sort

I am accustomed to winform TreeView having a Sorted property which automatically manages nodes sorting.
I now have to alphabetically sort an ASP.NET TreeView and I'm surprised I cannot find any similar property or callback method.
Is there any way to automatically achieve this operation in ASP.NET or do I have to manually sort and insert my nodes in correct order?
You'll need to write your own sorting function but its reasonably trivial to add this functionality.
http://blog.mdk-photo.com/post/C-Extentionmethod-Tree-Node-View-Sort().aspx
.NET 3.5 supports extension methods so you can add functionality to pre-existing System Classes. Notice the this syntax on the method parameter. More Info Here
public static void Sort(this TreeView tv)
{
TreeNodeCollection T = tv.Nodes.Sort();
tv.Nodes.Clear();
tv.Nodes.AddRange(T);
}
public static void Sort(this TreeNode tn)
{
TreeNodeCollection T = tn.ChildNodes.Sort();
tn.ChildNodes.Clear();
tn.ChildNodes.AddRange(T);
}
The first link contains the rest of the code you'll need to complete the sorting functionality
Three years later and the TreeView still doesn't support sorting natively. Here's a simple method to do sort all nodes alphabetically.
private void SortTreeNodes(TreeNodeCollection treeNodes)
{
var sorted = true;
foreach (TreeNode treeNode in treeNodes)
{
SortTreeNodes(treeNode.ChildNodes);
}
do
{
sorted = true;
for (var i = 0; i < treeNodes.Count - 1; i++)
{
var treeNode1 = treeNodes[i];
var treeNode2 = treeNodes[i + 1];
if (treeNode1.Text.CompareTo(treeNode2.Text) > 0)
{
treeNodes.RemoveAt(i + 1);
treeNodes.RemoveAt(i);
treeNodes.AddAt(i, treeNode2);
treeNodes.AddAt(i + 1, treeNode1);
sorted = false;
}
}
} while (!sorted);
}
Call it like this
SortTreeNodes(myTreeView.Nodes);

Resources