LoadOperation on the client side returns null? How can I fix it? Is my code correct? Is it a best practice?
Serverside (Domain service:
public IQueryable<State> GetStates()
{
return this.ObjectContext.States.Include("Country") ;
}
//-----------------------------------------------------------------------
Clientside
LoadOperation<State> loadOp;
public IEnumerable<State> Entities()
{
DSCommon _context = new DSCommon();
loadOp = _context.Load(_context.GetStatesQuery());
loadOp.Completed += complete;
loadOp.Completed += new EventHandler(LoadOp_Completed);
return loadOp.Entities;
}
EventHandler complete;
void LoadOp_Completed(object sender, EventArgs e)
{
foreach (var item in loadOp.Entities)
{
/************* item.Country is Null ********************/
}
}
Your question is not very clear as first you say that LoadOperation return null whereas in your code, you state that item.Country is null.
However, I believe that I see the problem.
In you Domain Service you call the Include("Country") method on the States EntityCollection. However, on client side, the State.Country Entity is still null? I had the same issue some time ago. It seems that RIA Services (or WCF) does not return those entities, unless you apply the [Include] attribute on the Entity you want to return like so in a metadata class
[MetadataType(typeof(State.StateMetadata))]
public partial class State
{
internal sealed class StateMetadata
{
private StateMetadata()
{
}
[Include]
public EntityCollection<Country> Country;
}
}
Someone will probably be able to give an explanation on why it works this way. I just know that I had to do it this way around :-)
Related
Background:
I am writing a WPF application, strictly following the MVVM pattern. I have a BaseRepository class as a generic interface for connecting to different databases (EF is not an option), and everything works fine; this is just a technical question.
I use a wrapped ObservableCollection called NotifyingCollection, to subscribe to IEditableObject's ItemEndEdit events (my ViewModelBase entity wrapper implements INotifyPropertyChanged and IEditableObject members).
The provided code sample throws an "'EditItem' is not allowed for this view" exception when the ReadAll method is called upon editing an item in my WPF DataGrid. However, if I replace the line in the method with the commented out part, it works perfectly!
Question:
In other words, it looks like relaying the Linq.Enumerable.Where extension method instead of returning an IEnumerable version of the collection removes functionality from the custom collection; why would that happen if they are both IEnumerable?
Code Sample:
namespace MyCompany.Common.Abstracts
{
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Data.Common;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using MyCompany.Common.Extensions;
using MyCompany.Common.Utilities;
public abstract class BaseRepository<TEntity> : IDisposable where TEntity : ViewModelBase
{
protected BaseRepository()
{
this.EntitySet = new NotifyingCollection<TEntity>();
this.EntitySet.ItemEndEdit += new ViewModelBase.ItemEndEditEventHandler(ItemEndEdit);
this.EntitySet.CollectionChanged += new NotifyCollectionChangedEventHandler(CollectionChanged);
}
protected abstract NotifyingCollection<TEntity> EntitySet { get; set; }
protected virtual void PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Debug.WriteLine(String.Format("Modify '{0}'", e.PropertyName), "PropertyChanged");
}
protected virtual void ItemEndEdit(IEditableObject sender)
{
this.Update(sender as TEntity);
}
protected virtual void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
var collection = (e.Action == NotifyCollectionChangedAction.Remove) ?
e.OldItems : e.NewItems;
foreach (TEntity entity in collection)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
entity.PropertyChanged += this.PropertyChanged;
this.Create(entity);
break;
case NotifyCollectionChangedAction.Remove:
entity.PropertyChanged -= this.PropertyChanged;
this.Delete(entity);
break;
default:
Debug.WriteLine(String.Format("{0} '{1}'", e.Action.ToString(), entity.DisplayName), "CollectionChanged");
break;
}
}
}
public virtual bool Create(TEntity entity)
{
Debug.WriteLine(String.Format("Create '{0}'", entity.DisplayName), "CollectionChanged");
return true;
}
public virtual IEnumerable<TEntity> Read(Expression<Func<TEntity, bool>> filter)
{
return this.EntitySet.Where(filter.Compile());
}
public virtual IEnumerable<TEntity> ReadAll()
{
return this.Read(x => true);
//return this.EntitySet;
}
public virtual bool Update(TEntity entity)
{
Debug.WriteLine(String.Format("Update '{0}'", entity.DisplayName), "ItemEndEdit");
return true;
}
public virtual bool Delete(TEntity entity)
{
Debug.WriteLine(String.Format("Delete '{0}'", entity.DisplayName), "CollectionChanged");
return true;
}
public virtual IEnumerable<TColumn> GetColumn<TColumn>(string columnName)
{
var lookupTable = this.Read(x => x != null);
List<TColumn> column = new List<TColumn>();
foreach (TEntity record in lookupTable)
{
column.Add(record.GetPropValue<TColumn>(columnName));
}
var result = column.Distinct();
foreach (TColumn element in result)
{
yield return element;
}
}
public abstract int Commit();
public abstract DbTransaction BeginTransaction();
public abstract void Dispose();
}
}
From MichaelLPerry's blog: Common mistakes while using ObservableCollection
ObservableCollection is imperative. Linq is declarative. The two
cannot be used together without extra help.
Imperative code explicitly acts upon something. When using an
ObservableCollection, you explicitly call Add, Remove, and other
methods to change the collection. You have to decide exactly when and
how to take these actions.
Declarative code implicitly generates something. When using linq, you
declare what a collection will look like, how it will be filtered,
mapped, and transformed. You don’t explicitly modify the collection.
These two paradigms don’t mix. Once you use linq on an
ObservableCollection, it is no longer observable. There are
open-source projects like Bindable Linq that bridge the gap. But
without that extra help, people are often surprised when things don’t
work.
The reurn value of EntitySet.Where method won't be the original NotifyingCollection<TEntity> anymore.
I am reading http://msdn.microsoft.com/en-us/magazine/dd419663.aspx tutorial
i don't understand what the below code is trying to do.
_saveCommand = new RelayCommand(param => this.Save(), param => this.CanSave );
As defined in the realy Command class CanSave should have been a Method with a paramter since it maps to predicate therefore it's correspoding method should have a paramter same is true for action object. Please help understand.
RelayCommand uses functions (more precisely, delegates) passed into its constructor to implement CanExecute and Execute methods.
In this sample two functions are passed. First describes how to save - just call Save method on RelayCommand owner. Another one describes, how to check if saving is possible - just check current state of owner's CanSave property.
In this way you don't have to create your own Command class explicitly.
UPD:
Thanks, but my questions is Save() is of type Action, defined as Action and as per my understanding Save() should have a parameter in order to work. But some reason it is able to work even without a paramter.
Ok, let's look at it closer.
_saveCommand = new RelayCommand(param => this.Save(), param => this.CanSave );
is equivalent of (in syntax of C# v2.0)
_saveCommand = new RelayCommand(
new Action<object>(delegate(object param){ this.Save(); }),
new Func<object,bool>(delegate(object param){ return this.CanSave; }));
So, you create anonymous functions wrap actual methods leaving to you right to use or not to use their own parameters.
If you want to go deeper, the code above is compiled under the hood to something like:
// it is OK to ignore methods arguments.
// So, it's also OK to ignore them in anonymous methods as well
private void Save_Anonymous(object parameter){
this.Save();
}
private bool CanSave_Anonymous(object parameter){
return this.CanSave;
}
....
_saveCommand = new RelayCommand(new Action<object>(this.Save_Anonymous),
new Func<object, bool>(this.CanSave_Anonymous));
Note that compiler can select other strategies for implementing delegates, depending on what values they enclose from surrounding context. E.g. if your anonymous functions referenced some local variables compiler would generate anonymous class that contained those variables and put methods in this class.
Let us simplify it
First off, RelayCommand is not part of WPF. It is a class inside the WPFlight toolkit, and we are free to write our own implementations of it.
It acts as a wrapper on top of the WPF ICommand, and provides two aspects: action and predicate. The predicate part can be used, for example to enable or disable a button based on some condition. The action part shall contain the logic that should run when the command is executed.
As with most concepts, this too has many possible approaches.
Approach 1
Write explicit named methods for action and predicate. Sample code below
class demoViewModel
{
string filename = "";
private ICommand _saveCommand;
public ICommand SaveCommand
{
get
{
if (_saveCommand== null)
{
_saveCommand = new RelayCommand<object>(
action => Save(filename),
predicate => CanSave(filename));
}
return _saveCommand;
}
}
private bool CanSave(string fname)
{
return (!string.IsNullOrEmpty(fname));
}
private void Save(string fname)
{
SaveHelper(fname);//some logic goes inside SaveHelper
}
}
Approach 2
Here we shall use anonymous methods. This reduces many lines of code and makes the whole code more readable.
class demoViewModel1
{
string filename = "";
private ICommand _saveCommand;
public ICommand SaveCommand
{
get
{
if (_saveCommand== null)
{
_saveCommand = new RelayCommand<object>(
action => { SaveHelper(filename); },
predicate => { return (!string.IsNullOrEmpty(filename)); }
);
}
return _saveCommand;
}
}
}
Approach 3
Make use of lambda expressions, well, almost fully
class demoViewModel2
{
string filename = "";
private ICommand _saveCommand;
public ICommand SaveCommand
{
get
{
if (_saveCommand== null)
{
_saveCommand = new RelayCommand<object>(
(objParamForAction) => { SaveHelper(filename); },
() => { return (!string.IsNullOrEmpty(filename)); }
);
}
return _saveCommand;
}
}
}
Whats the best way to save variables like userid that is stored and reachable from different pages in WP7.
There's the querystring method, but can be kind of a pain to implement.
When navigating, pass the parameter like a HTTP querystring.
Then, on the otherside, check if the key exists, and extract the value. The downside of this is if you need to do more than 1, you need to type it in yourself, and it only supports strings.
So to pass an integer, you'd need to convert it. (And to pass a complex object, you need to take all the pieces you need to recompile it on the other side)
NavigationService.Navigate(new Uri("/PanoramaPage1.xaml?selected=item2", UriKind.Relative));
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
string selected = String.Empty;
//check to see if the selected parameter was passed.
if (NavigationContext.QueryString.ContainsKey("selected"))
{
//get the selected parameter off the query string from MainPage.
selected = NavigationContext.QueryString["selected"];
}
//did the querystring indicate we should go to item2 instead of item1?
if (selected == "item2")
{
//item2 is the second item, but 0 indexed.
myPanorama.DefaultItem = myPanorama.Items[1];
}
base.OnNavigatedTo(e);
}
Here's a sample app that uses a querystring.
http://dl.dropbox.com/u/129101/Panorama_querystring.zip
A easier (and better) idea is to define a variable globally, or use a static class. In App.xaml.cs, define
using System.Collections.Generic;
public static Dictionary<string,object> PageContext = new Dictionary<string,object>;
Then, on the first page, simply do
MyComplexObject obj;
int four = 4;
...
App.PageContext.Add("mycomplexobj",obj);
App.PageContext.Add("four",four);
Then, on the new page, simply do
MyComplexObj obj = App.PageContext["mycomplexobj"] as MyComplexObj;
int four = (int)App.PageContext["four"];
To be safe, you should probably check if the object exists:
if (App.PageContext.ContainsKey("four"))
int four = (int)App.PageContext["four"];
You may use an App level variable (defined in App.xaml.cs) and access it from anywhere within your app. If you want to persist, shove it into Isolated Storage and read it on App launch/activate. There are helpers available to JSon serialize/deserialize your reads/writes from the Isolated Storage.
Check out Jeff's post (here) on tips to use Isolated Storage.
Hope this helps!
Well "best" is always subjective, however, I think an application service is a good candidate for this sort of thing:-
public interface IPhoneApplicationService : IApplicationService
{
string Name {get; set;}
object Deactivating();
void Activating(object state);
}
public class AuthenticationService : IPhoneApplicationService
{
public static AuthenticationService Current {get; private set; }
public void StartService(ApplicationServiceContext context)
{
Current = this;
}
public void StopService()
{
Current = null;
}
public string Name {get; set;}
public object Deactivating()
{
// Return an serialisable object such as a Dictionary if necessary.
return UserID;
}
public void Activating(object state)
{
UserID = (int)state;
}
public int UserID { get; private set; }
public void Logon(string username, string password)
{
// Code here that eventually assigns to UserID.
}
}
You place an instance of this in your App.xaml:-
<Application.ApplicationLifetimeObjects>
<!--Required object that handles lifetime events for the application-->
<shell:PhoneApplicationService
Launching="Application_Launching" Closing="Application_Closing"
Activated="Application_Activated" Deactivated="Application_Deactivated"/>
<local:AuthenticationService Name="AuthServ" />
</Application.ApplicationLifetimeObjects>
Now you do need to tweak the App.xaml.cs:-
private void Application_Activated(object sender, ActivatedEventArgs e)
{
var state = PhoneApplicationService.Current.State;
foreach (var service in ApplicationLifetimeObjects.OfType<IPhoneApplicationService>())
{
if (state.ContainsKey(service.Name))
{
service.Activating(state[service.Name]);
}
}
}
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
var state = PhoneApplicationService.Current.State;
foreach (var service in ApplicationLifetimeObjects.OfType<IPhoneApplicationService>())
{
if (state.ContainsKey(service.Name))
{
state[service.Name] = service.Deactivating();
}
else
{
state.Add(service.Name, service.Deactivating());
}
}
}
You can now access you UserID anywhere in your app with:-
AuthenticationService.Current.UserID
This general pattern can be used to maintain seperation of key application wide services (you don't load a whole bunch of incohesive properties into your App class). It also provides the hooks for maintaining state between activations which is essential.
I get the above error sometimes during the read. The exception originates from ASP.NET SqlDataReader whenever you try to read data before calling the Read() method. Since EF does all these internally, I am wondering what else can cause this error. could it be network (or) db connectivity?
thanks
Additional Bounty Info (GenericTypeTea):
I've got the same error after upgrading to EF Code First RC (4.1):
"Invalid attempt to read when no data
is present"
This is the code in question:
using (var context = GetContext())
{
var query = from item in context.Preferences
where item.UserName == userName
where item.PrefName == "TreeState"
select item;
// Error on this line
Preference entity = query.FirstOrDefault();
return entity == null ? null : entity.Value;
}
The table structure is as follows:
Preference
{
Username [varchar(50)]
PrefName [varchar(50)]
Value [varchar(max)] Nullable
}
The table is standalone and has no relationships. This is the DbModelBuilder code:
private void ConfigurePreference(DbModelBuilder builder)
{
builder.Entity<Preference>().HasKey(x => new { x.UserName, x.PrefName });
builder.Entity<Preference>().ToTable("RP_Preference");
}
Exactly the same code works perfectly in CTP5. I'm guessing this is an RC bug, but any ideas of how to fix it would be appreciated.
This error occurs when there is a large amount of data in the RC release. The difference between the RC and CTP5 is that you need to specify the [MaxLength] property that contains a large amount of data.
Are you re-using contexts? I would guess this is happening as a result of something you are doing within GetContext
If GetContext() provides a stale context, in which the DataReader is closed/corrupted, I could see the above happening.
I cannot reproduce your problem on EF4.1 RC1.
POCO:
public class Preference
{
public string UserName { get; set; }
public string PrefName { get; set; }
public string Value { get; set; }
}
Context:
public class PreferenceContext : DbContext
{
public DbSet<Preference> Preferences {get;set;}
public PreferenceContext()
: base("Data Source=localhost;Initial Catalog=_so_question_ef41_rc;Integrated Security=SSPI;") {
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
ConfigurePreference(modelBuilder);
base.OnModelCreating(modelBuilder);
}
private void ConfigurePreference(DbModelBuilder builder)
{
builder.Entity<Preference>().HasKey(x => new { x.UserName, x.PrefName });
builder.Entity<Preference>().ToTable("RP_Preference");
}
}
My little Console App:
class Program
{
static void Main(string[] args)
{
string userName = "Anon";
for (int i = 0; i < 10000; i++)
{
var p = GetPreference(userName);
}
}
private static string GetPreference(string userName)
{
using (var context = new PreferenceContext())
{
var query = from item in context.Preferences
where item.UserName == userName
where item.PrefName == "TreeState"
select item;
// Error on this line
Preference entity = query.FirstOrDefault();
return entity == null ? null : entity.Value;
}
}
}
I do 10,000 reads, and no error. You will need to post more complete code to continue.
Increase the CommandTimeout on the context.
I had the same issue with EF4 - In my case I was (trying to) return the list of entities within the using{} section. This is the same as you are doing in your question:
return entity == null ? null : entity.Value;
} // end using
I moved the return to after the } and it worked.
I think I had the problem because the code was in a function which had already queried the database in another using block, I suspect the table was locking but not reporting the error, ending the using block before the return released the database lock.
Steve
I'm trying to drag data from the Winforms portion of my application on a WPF controls that's contained inside an "ElementHost". And it crashes when I try doing so.
Trying the same thing but from Winforms to Winforms works fine. (See example code below)
I need help on making this work... have any clues what I'm doing wrong?
Thanks!
Example:
In the sample code below, I'm just trying to drag a custom MyContainerClass object created when initating the drag on the label control on a 1) System.Windows.Forms.TextBox (Winforms) and 2) System.Windows.TextBox (WPF, added to an ElementHost).
Case 1) works fine but case 2) is crashing when trying to retrieve the drop data using GetData(). GetDataPresent("WindowsFormsApplication1.MyContainerClass") returns "true" so In theory, I should be able to retrive my drop data of that type like in Winforms.
Here is the stack trace of the crash:
"Error HRESULT E_FAIL has been returned from a call to a COM component" with the following stack trace:
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
at System.Windows.Forms.DataObject.GetDataIntoOleStructs(FORMATETC& formatetc, STGMEDIUM& medium)
at System.Windows.Forms.DataObject.System.Runtime.InteropServices.ComTypes.IDataObject.GetDataHere(FORMATETC& formatetc, STGMEDIUM& medium)
at System.Windows.Forms.DataObject.System.Runtime.InteropServices.ComTypes.IDataObject.GetData(FORMATETC& formatetc, STGMEDIUM& medium)
at System.Windows.DataObject.OleConverter.GetDataInner(FORMATETC& formatetc, STGMEDIUM& medium)
at System.Windows.DataObject.OleConverter.GetDataFromOleHGLOBAL(String format, DVASPECT aspect, Int32 index)
at System.Windows.DataObject.OleConverter.GetDataFromBoundOleDataObject(String format, DVASPECT aspect, Int32 index)
at System.Windows.DataObject.OleConverter.GetData(String format, Boolean autoConvert, DVASPECT aspect, Int32 index)
at System.Windows.DataObject.OleConverter.GetData(String format, Boolean autoConvert)
at System.Windows.DataObject.GetData(String format, Boolean autoConvert)
at System.Windows.DataObject.GetData(String format)
at WindowsFormsApplication1.Form1.textBox_PreviewDragEnter(Object sender, DragEventArgs e) in WindowsFormsApplication1\WindowsFormsApplication1\Form1.cs:line 48
Here is some code:
// -- Add an ElementHost to your form --
// -- Add a label to your form --
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
System.Windows.Controls.TextBox textBox = new System.Windows.Controls.TextBox();
textBox.Text = "WPF TextBox";
textBox.AllowDrop = true;
elementHost2.Child = textBox;
textBox.PreviewDragEnter += new System.Windows.DragEventHandler(textBox_PreviewDragEnter);
System.Windows.Forms.TextBox wfTextBox = new System.Windows.Forms.TextBox();
wfTextBox.Text = "Winforms TextBox";
wfTextBox.AllowDrop = true;
wfTextBox.DragEnter += new DragEventHandler(wfTextBox_DragEnter);
Controls.Add(wfTextBox);
}
void wfTextBox_DragEnter(object sender, DragEventArgs e)
{
bool dataPresent = e.Data.GetDataPresent("WindowsFormsApplication1.MyContainerClass");
// NO CRASH here!
object data = e.Data.GetData("WindowsFormsApplication1.MyContainerClass");
}
void textBox_PreviewDragEnter(object sender, System.Windows.DragEventArgs e)
{
bool dataPresent = e.Data.GetDataPresent("WindowsFormsApplication1.MyContainerClass");
// Crash appens here!!
// {"Error HRESULT E_FAIL has been returned from a call to a COM component."}
object data = e.Data.GetData("WindowsFormsApplication1.MyContainerClass");
}
private void label1_MouseDown(object sender, MouseEventArgs e)
{
label1.DoDragDrop(new MyContainerClass(label1.Text), DragDropEffects.Copy);
}
}
public class MyContainerClass
{
public object Data { get; set; }
public MyContainerClass(object data)
{
Data = data;
}
}
#Pedery & jmayor: Thanks for the suggestions guys! (see my findings below)
After quite a few experimentation, trials and errors, and a bit of "Reflector'ing", I managed to figure out exactly why I was receiving the cryptic error message "Error HRESULT E_FAIL has been returned from a call to a COM component".
It was due to the fact that when dragging data WPF <-> Winforms in a same app, that data has to be Serializable!
I've checked how difficult it would be to transform all of our classes to "Serializable" and I would have a been a real pain for a couple of reasons... one, we would need to practically make all of classes serializable and two, some of these classes have references to Controls! And Controls aren't serializable. So a major refactoring would have been needed.
So... since we wanted to pass any object of any class to drag from/to WPF inside the same application, I decided to create a wrapper class, with the Serializable attribute and implementing ISerializable. I would have 1 contructor with 1 parameter of type "object" which would be the actual drag data. That wrapper, when serializing/de-serializing, would serialize not the object itself... but rather the IntPtr to the object (which we can do since we only want that functionnality inside our 1 instance only application.) See code sample below:
[Serializable]
public class DataContainer : ISerializable
{
public object Data { get; set; }
public DataContainer(object data)
{
Data = data;
}
// Deserialization constructor
protected DataContainer(SerializationInfo info, StreamingContext context)
{
IntPtr address = (IntPtr)info.GetValue("dataAddress", typeof(IntPtr));
GCHandle handle = GCHandle.FromIntPtr(address);
Data = handle.Target;
handle.Free();
}
#region ISerializable Members
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
GCHandle handle = GCHandle.Alloc(Data);
IntPtr address = GCHandle.ToIntPtr(handle);
info.AddValue("dataAddress", address);
}
#endregion
}
To keep the IDataObject functionnality, I created the following DataObject wrapper:
public class DataObject : IDataObject
{
System.Collections.Hashtable _Data = new System.Collections.Hashtable();
public DataObject() { }
public DataObject(object data)
{
SetData(data);
}
public DataObject(string format, object data)
{
SetData(format, data);
}
#region IDataObject Members
public object GetData(Type format)
{
return _Data[format.FullName];
}
public bool GetDataPresent(Type format)
{
return _Data.ContainsKey(format.FullName);
}
public string[] GetFormats()
{
string[] strArray = new string[_Data.Keys.Count];
_Data.Keys.CopyTo(strArray, 0);
return strArray;
}
public string[] GetFormats(bool autoConvert)
{
return GetFormats();
}
private void SetData(object data, string format)
{
object obj = new DataContainer(data);
if (string.IsNullOrEmpty(format))
{
// Create a dummy DataObject object to retrieve all possible formats.
// Ex.: For a System.String type, GetFormats returns 3 formats:
// "System.String", "UnicodeText" and "Text"
System.Windows.Forms.DataObject dataObject = new System.Windows.Forms.DataObject(data);
foreach (string fmt in dataObject.GetFormats())
{
_Data[fmt] = obj;
}
}
else
{
_Data[format] = obj;
}
}
public void SetData(object data)
{
SetData(data, null);
}
#endregion
}
And we are using the above classes like this:
myControl.DoDragDrop(new MyNamespace.DataObject(myNonSerializableObject));
// in the drop event for example
e.Data.GetData(typeof(myNonSerializableClass));
I know I know... it's not very pretty... but it's doing what we wanted. We also created a dragdrop helper class which masks the DataObject creation and has templated GetData functions to retrieve the data without any cast... a bit like:
myNonSerializableClass newObj = DragDropHelper.GetData<myNonSerializableClass>(e.Data);
So thanks again for the replies! You guys gave me good ideas where to look at for possible solutions!
-Oli
I had a "similar" issue some time ago so I can at least tell you what I found out.
It seems .Net is resorting to OLE remoting when drag/drop operations are performed in but the simplest of cases. For some reason GetDataPresent will in these cases be successful and GetData will fail. This is furthermore mystified by the fact that there are several versions of the IDataObject in the .Net framework.
Windows Forms defaults to System.Windows.Forms.IDataObject. However, in your case you could try to give System.Runtime.InteropServices.ComTypes.IDataObject a shot instead. You can also check out my discussion here.
Hope this helps.
Seems wonderfull at first sight. I tried it but got some errors on implementations.
I began to correct some errors when I decided to look for something a little bit more simplier, that do not have pointers (humm I don't like that, particularly with carbage collection, but I have no idea if it could have real impact) and that do not use Interop.
I come up with that. It works for me and I hope it will work for anybody else. It is only intended to be used for local drag drop (inside the same app).
How to use to drag:
DragDrop.DoDragDrop(listBoxOfAvailableScopes, new DragDropLocal(GetSelectedSimulResultScopes()),
DragDropEffects.Copy);
How to use to drop (get):
DragDropLocal dragDropLocal = (DragDropLocal)e.Data.GetData(typeof(DragDropLocal));
SimulResultScopes simulResultScopes = (SimulResultScopes)dragDropLocal.GetObject();
Code:
namespace Util
{
[Serializable]
public class DragDropLocal
{
private static readonly Dictionary<Guid, object> _dictOfDragDropLocalKeyToDragDropSource = new Dictionary<Guid, object>();
private Guid _guid = Guid.NewGuid();
public DragDropLocal(object objToDrag)
{
_dictOfDragDropLocalKeyToDragDropSource.Add(_guid, objToDrag);
}
public object GetObject()
{
object obj;
_dictOfDragDropLocalKeyToDragDropSource.TryGetValue(_guid, out obj);
return obj;
}
~DragDropLocal()
{
_dictOfDragDropLocalKeyToDragDropSource.Remove(_guid);
}
}
}
Maybe the events are in the opposite way. The PreviewDragEnter should be related with the WPFTextBox. Also watch out the DragEventArgs class. There is one in System.Windows.Form ( Windows Form version) and the one under System.Windows( for WPF version).