locked
Silverlight 4.0 is seriously flawed RRS feed

  • Question

  • This is my second post on this in as many days.  Silverlight 4.0 is not worth a dime if a user can't type in text on a site and paste images into a richtextbox without ability to persist it.

    Alas, this is the dead end road of Silverlight's support for RichTextBox.  The RichTextBox at a minimum should have ability to persist as a byte array everything it contains.  This is because RIA support only has btye types available.  Alas it has nothing.  You can't even serialize it. 

    So with such a fundamental blunder in SilverLight Support wouldn't it be safe to say that Silverlight is almost worthless for user input?  IMO the answer is yes. 

    Perhaps HTML 5.0 is the death toll for Silverlight, too bad it takes a consortium of other companies to solidify something that works while the rest of us loyal MSFT developers get the shaft for picking Silverlight.  Adobe's PDF support on the web sets the examples as well as current HTML Richtextbox support found everywhere.  Forget MSFT's Silverlight solution.

    Tuesday, April 5, 2011 6:48 AM

Answers

  • Ok found the save solution as follows:

    I created a ImageText class.  It is simply a Bitmap image and Text.  The text is for the caption.  The class can run in a RichTextBox and when in read mode allows the user to resize the image. Cool...

    Problem was I couldn't figure out how to just save RTB.  As per this discussion it can't be done directly.

    So what I did was allow the user to use the OpenFileDialog.  When they do this, I do a conversion to bytes in the ImageTextClass:

    The var Bytes is global and has a public accessor GetBytes();

     private void convertToByte(FileInfo info)
            {
                try {
                    FileStream fs = info.OpenRead();    
                using (fs)
                {
                    // Read the source file into a byte array.
                    bytes = new byte[fs.Length];
                    int numBytesToRead = (int)fs.Length;
                    int numBytesRead = 0;
                    while (numBytesToRead > 0)
                    {
                        // Read may return anything from 0 to numBytesToRead.
                        int n = fs.Read(bytes, numBytesRead, numBytesToRead);
     
                        // Break when the end of the file is reached.
                        if (n == 0)
                            break;
     
                        numBytesRead += n;
                        numBytesToRead -= n;
                    }
                    numBytesToRead = bytes.Length;
                }
                }
                catch (Exception iox) { 
                    //TODO:Log this error
                    string dummy = iox.Message;
                }
            }
    So now that the IMAGETEXT object contains the bytes and I want to save the image to a RIA Backend, I can do this:
    #######################################################################################
    private void SaveImageAsynchronously(ImageText it)
    {
        Web.ImageText imagetext = new Web.ImageText();             
        imagetext.ImageBitMap = it.GetBtyes();
        imagetext.ImageName= it.ImageName;  
            if (imagetext.HasValidationErrors)
                {
                    foreach (var thing in imagetext.ValidationErrors)
                    {
                        System.Diagnostics.Debugger.Break();
                    }
                    return;
                }           
        Web.DomainService1 ds1 = new Web.DomainService1();  
        LoadOperation load = ds1.Load(ds1.GetImageTextsQuery());
        load.Completed += (o, s) =>
        {
            try {
                     
                ds1.ImageTexts.Add(imagetext);
                ds1.SubmitChanges();
                        
            }catch(Exception iox){
                //TODO:Log this IOX
                string dummy = iox.Message;
            }
        };
    }
     
    I don't have it fully asynchronous yet but will using the domain.client.   The database table has just three fields.  ID, ImageName (Varchar)
    and ImageBitmap (Byte)
     
    I was able to get the byte of image to DB.  Now as far as parsing the InlineUIContainer, I've been able to do that too, but...haven't yet figured out how 
    to resinsert into RTB datastream... I suspect I need to plug in placholder XAML that will do substitution in a recontruction of the RTB and images from
    the database.   
     
    P.S. I was never angry about this, just noisy.  Still this circumvention is just that and it requires a lot (way too much) internals knowledge on the layout 
    of a richtextbox. 
    Tuesday, April 5, 2011 2:02 PM

All replies

  • Well, it would be nice to have more features if those you mention are missing.  But I'm quite sure you are "tossing out the baby with the bath water".  Silverlight has a couple of more features than RichtextBox (which I haven't had a need to use yet).  So I wouldn't condemn the entire framework because of one weak user control.

    But thanks for venting.

    Tuesday, April 5, 2011 7:05 AM
  • Hi. I've read your first topic also, and I'm not sure why you start a second thread like this, because you're not really adding anything new. I understand that you are upset because a control in the library does not offer the exact feature you were looking for, but like the previous writer I think Silverlight offers way more than rich text editing, and codemning the whole technology due to the ToByteArray() method you're missing seems a tad overboard :).

    Back on topic one has to say that just because there is no convenient method for that, you can still do all that you want with some programming effort when you access the Blocks property and serialize your content manually.

    A much more comfortable alternative is to use a 3rd party component that has support for this. For example, Telerik's RadRichTextBox has import and export support for a variety of formats like XAML, Html, docx etc. and serializes any of its content (including images etc.); it also comes with built-in support for flow layout, something you're apparently also looking for.

    I don't want to overly advertise this; I just want to emphasize that it has always been this way: Microsoft provides a solid base collection of controls you can built appealing applications with, but they do not try to provide jack-of-all-trades libraries. This is not only true for Silverlight, but with any other of their technologies I have worked with. The reason of course is not that they couldn't do that. The market for 3rd party controls, libraries and components always has been an important one, and there's no point in ruining it for a lot of companies that depend on selling their software.



    Tuesday, April 5, 2011 7:42 AM
  • Peter;

    I rewrote the topic for visibility to MSFT as they are slient on this issue, just like the lack of function in RTB.  I haven't as you've assumed thrown the baby out either.  I'm just making a definitive statement regarding  the lack of user RichText via Images into RTB to be persisted.  That's a serious flaw.

    Mr. Goodcat, I've read and respect many of your posts, but you're off base on this one.  User posts an image into a RTB on the web and Silverlight can't save it?  While ok in early 90's or late 80's not okay in 2011.  This is just a completely ridiculous position.  How can MSFT go this long without this most fundamental support?  It's just ridiculous, that's all.

    I'm mostly irritated at this because I spent three weeks tooling up for Silverlight when I had the function already in WPF.  Only to find I wasted three weeks.  Is that what Silverlight team wants is extensive analysis by developers prior to picking framework just to see if we can Save things?  I don't think so...

    Tuesday, April 5, 2011 8:04 AM
  • I agree that it would be nice to have built-in support for that. I also understand your anger when you found it it's easily possible in WPF but not in Silverlight, and I probably would also be annoyed if I had spent some time on this without finding a quick way of solving it.

    But to be honest I don't see why there should be an official statement on this. I don't recall anybody making claims as to the RichTextBox behaving identical to e.g. the one in WPF, and the documentation doesn't make promises about serialization of the whole content either. In fact, it is pretty clear on the limitation, as it says that inline UI elements are not supported and converted to empty runs when you serialize the content as XAML. While it may come as a surprise to find an option like this missing, I indeed believe that it's part of our jobs (maybe not the developers, but at least the software designers and architects etc.) to analyze whether the framework, libraries and technologies we intend to use are suitable for the job. I have spent weeks with evaluation through-out my projects just to check what can be used, what additional software or components need to be bought, what might lead to problems later etc. before the actual kick-off. Of course it sometimes happens that one overlooks certain details, but especially in this case it is not really a show-stopper as both workarounds and alternatives exist. Like I said, just because there's no one-click solution to it doesn't mean it's not possible.


    Tuesday, April 5, 2011 8:24 AM
  • Yes and you are correct that the three weeks was really what it took to prototype this thing only to fail.  So in essence, the work was research.  But still, bad news in the end and surprizing that you can add INLINEUICONTAINERs with images et. al. easily but you can't persist out of the gate...

    But here's the catch Peter, if one uses Openfile dialog to retrieve and save images, there's no problems.  But because they've stripped SL support this is the only way avaialble.

    So let's say I can parse all the INLINEUICONTAINERS and get the image.  I can't even use the OpenfileDialog to get the Byte[] because, I can save the image.  Catch-22.  We have isolated storage ability but no control serialization?  What's the isolated storage for?  We have RIA support to serve images but we can't write back without an OpenFileDialog to get the byte[] ?  But most of all the Image and BitmapImage support that is in WPF is mysteriously missing in SilverLight..   Even when going to OOB using legit. certificates.

     

    Tuesday, April 5, 2011 9:02 AM
  • It beats me how anybody could get so upset, and make such wide-sweeping accusations ("silverlight is seriously flawed") when hundreds of thousands of people use it every day quite successfully. Your points about lack of certain functionality may be valid but sadly they are lost inside such a post.

    Writing like that is a sure-fire way of ensuring that anybody who matters will disregard your feedback.

    Tuesday, April 5, 2011 9:51 AM
  • Tim;

    I respect your opinion..  BTW, I am making progress on a solution... still, this is a serious omission in my opinion and I ask you respect my opinion as well, the headline banner was put in there for a reason.

    Remember: Just because someone says "The bug doesn't happen on my machine, it doesn't mean there's not a bug"  In similar fashion, for this problem, the fact that even millions of people are using SL successfully has no impact. 

    Tim this post wasn't really meant for any apologist of SL, unless they can help me find a solution.  Rather this post was a warning to anyone thinking of doing same thing in future or...perhaps even MSFT's advice or information if this will make it into a fix somewhere, someday.

    Tuesday, April 5, 2011 11:44 AM
  • Maybe you should post a bug/suggestion in the official place rather than the newsgroup.  That would probably get more Microsoft attention.

    Who knows, maybe it is already in the bug list.

    Tuesday, April 5, 2011 12:45 PM
  • There's no question that Microsoft does some really really stupid stuff occasionally, but who doesn't? For example, the SaveFileDialog doesn't let you set the file name. It would take them <60 seconds to fix it. But that doesn't mean the entire runtime is worthless. In addition, what's the alternative? HTML (including 5) can't do anything like this, and it isn't even planned. So at the earliest, to be able to do it in HTML you're looking at 2030. And really, that's no exaggeration. Hopefully, the SaveFileDialog will be corrected in v5 when someone at Microsoft gets around to typing:

    set { txtFileName.Text = value; }

    I get the desire to vent, but the "seriously flawed" headline is a little overboard. Now if you had an inline template issue...

    Tuesday, April 5, 2011 12:47 PM
  • After reading your last reply to me, I think I may see what the core of the problem is here. If you are interested in finding a solution to your problem, I am willing to help you sort it out.


    you can add INLINEUICONTAINERs with images et. al. easily but you can't persist out of the gate

    We have isolated storage ability but no control serialization?


    A lot of the problems people are struggling with in Silverlight comes from them focusing on the visual elements. For example, when they want to select an entry in a list box, their instinct is to get access to the list box item and check some checkbox. Silverlight however is very data-oriented. Your logic should always rely on the data, not on visual elements, and visual appearance should be handled through templating, styles, and data binding. If you focus on the visual part, you struggle. If you are willing to let go of this approach and focus on working with your data, a lot of things become much easier.

    I now realize that what you are trying to do is serialize the controls to get a binary representation of the RichTextBox content that you can store and retrieve later on, and I see how and why this of course is a problem. I spent the last 20 minutes on a way to serialize the RichTextBox content to a binary representation and restore it again, and I was able to create something that succeeds in doing so for unformatted text and images(!) in less than 200 lines of code. Of course it's not very pretty, lacks some error handling, and adding support for text formatting etc. would require some more work - but it's only meant to be a proof-of-concept, and as such it works, after 20 minutes.

    How did I do that? I concentrated on the data and ignored the visual elements altogether. I didn't try to serialize the controls, for example. I simply pulled the original image out of the image control and serialized that instead (as byte array). I got the text from the Run elements and converted it to bytes instead of trying to serialize the Run elements themselves. And so on. Like I said: there's no one-liner that does all this for you out of the box, it does require custom code. But it is possible, and with a slightly different thinking and approach it's even done in no time.




    Tuesday, April 5, 2011 1:48 PM
  • Ok found the save solution as follows:

    I created a ImageText class.  It is simply a Bitmap image and Text.  The text is for the caption.  The class can run in a RichTextBox and when in read mode allows the user to resize the image. Cool...

    Problem was I couldn't figure out how to just save RTB.  As per this discussion it can't be done directly.

    So what I did was allow the user to use the OpenFileDialog.  When they do this, I do a conversion to bytes in the ImageTextClass:

    The var Bytes is global and has a public accessor GetBytes();

     private void convertToByte(FileInfo info)
            {
                try {
                    FileStream fs = info.OpenRead();    
                using (fs)
                {
                    // Read the source file into a byte array.
                    bytes = new byte[fs.Length];
                    int numBytesToRead = (int)fs.Length;
                    int numBytesRead = 0;
                    while (numBytesToRead > 0)
                    {
                        // Read may return anything from 0 to numBytesToRead.
                        int n = fs.Read(bytes, numBytesRead, numBytesToRead);
     
                        // Break when the end of the file is reached.
                        if (n == 0)
                            break;
     
                        numBytesRead += n;
                        numBytesToRead -= n;
                    }
                    numBytesToRead = bytes.Length;
                }
                }
                catch (Exception iox) { 
                    //TODO:Log this error
                    string dummy = iox.Message;
                }
            }
    So now that the IMAGETEXT object contains the bytes and I want to save the image to a RIA Backend, I can do this:
    #######################################################################################
    private void SaveImageAsynchronously(ImageText it)
    {
        Web.ImageText imagetext = new Web.ImageText();             
        imagetext.ImageBitMap = it.GetBtyes();
        imagetext.ImageName= it.ImageName;  
            if (imagetext.HasValidationErrors)
                {
                    foreach (var thing in imagetext.ValidationErrors)
                    {
                        System.Diagnostics.Debugger.Break();
                    }
                    return;
                }           
        Web.DomainService1 ds1 = new Web.DomainService1();  
        LoadOperation load = ds1.Load(ds1.GetImageTextsQuery());
        load.Completed += (o, s) =>
        {
            try {
                     
                ds1.ImageTexts.Add(imagetext);
                ds1.SubmitChanges();
                        
            }catch(Exception iox){
                //TODO:Log this IOX
                string dummy = iox.Message;
            }
        };
    }
     
    I don't have it fully asynchronous yet but will using the domain.client.   The database table has just three fields.  ID, ImageName (Varchar)
    and ImageBitmap (Byte)
     
    I was able to get the byte of image to DB.  Now as far as parsing the InlineUIContainer, I've been able to do that too, but...haven't yet figured out how 
    to resinsert into RTB datastream... I suspect I need to plug in placholder XAML that will do substitution in a recontruction of the RTB and images from
    the database.   
     
    P.S. I was never angry about this, just noisy.  Still this circumvention is just that and it requires a lot (way too much) internals knowledge on the layout 
    of a richtextbox. 
    Tuesday, April 5, 2011 2:02 PM
  • If you really want to serialize the RTB, let me point you to this.

    From David Poll's site, an article - To XAML, with love (an experiment with XAML Serialization in Silverlight)

    Wednesday, April 6, 2011 5:14 AM
  • That is a great link, thank you for sharing.


    Wednesday, April 6, 2011 5:42 AM
  • Agreed, David, thanks for spotting that link.  I hope that MSFT will take a look into this.

    Should I create a Technet report for enhancement request?

     

    Thursday, April 7, 2011 9:28 AM
  • A search at SilverlightCream.com for "Serial" popped out 15 links, one of them the XAML Serialization one.

    Searching for "Rich Text" and Silverlight 4 found this one:

    http://www.silverlightshow.net/items/Working-with-the-Silverlight-Rich-Text-Box-control.aspx

    -Dave

    Friday, April 8, 2011 10:28 AM
  • Hi Dave;

    I saw that solution two weeks ago and zeroed in on this code below.  Note that in their example they omit InlineUIContainers which is exactly what this thread is about...

     

    Tx!

    private void Save_Click(object sender, RoutedEventArgs e)
    {

        // we do not save UI Elements
        var uiElements = from block in rbtMyRichTextBox.Blocks
                  from inline in (block as Paragraph).Inlines
                  where inline.GetType() == typeof(InlineUIContainer)
                  select inline;

        if (uiElements.Count() != 0)
        {
            MessageBox.Show("UIElements cannot be saved");
            return;
        }

        SaveFileDialog sfdSaveXaml = new SaveFileDialog();
        sfdSaveXaml.DefaultExt = ".sav";
        sfdSaveXaml.Filter = "Saved rtb files|*.rtb";

        if (sfdSaveXaml.ShowDialog().Value)
        {
            using (FileStream fs = (FileStream)sfdSaveXaml.OpenFile())
            {
                System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding();
                // the Xaml is what we want to save
                byte[] buffer = enc.GetBytes(rbtMyRichTextBox.Xaml);
                fs.Write(buffer, 0, buffer.Length);
                fs.Close();
            }
        }
    }

    Friday, April 8, 2011 1:21 PM
  • I've had the same issue with RTB and got very frustrated. 

    I wonder the new text features from SL 5 will be able to solve (at least part of) the problem? 

    Wednesday, April 13, 2011 10:00 PM
  • The more I dig into this the more I realize that .NET has never fully supported RichTextBox....  How can I say that?  If you go to MSFT's own site and look at the 1999 specs. for RichText 1.7 protocol you'll find a zillion properties, methods/ways of marking up RichText that are nowhere to be found in the subset of controls that MSFT supports. 

    The RichText Protocol is huge....  What MSFT did instead to give user's .NET like function is to add the INLINEUICONTAINER.  All a seeminly beautiful thing to a developer's eye (at first take).  But when it get's into Persisting this, buyer-beware because you are in for a ride...

    Here's the "Circumvention Design" for this gross oversight. 

    • You have to parse all inlines specifically looking for INLINEUICONTAINER
    • You have to handle each type of INLINEUICONTAINER using special customized code that does this.
    • Determine the type and save it accordingly as either a byte[] or serilized type to backend.
    • Place a marker in the content at that point that will allow you to find this location and reinsert this container when recreating this for the user to edit/read.

    When recreating the saved RTB, you have to parse everything looking for the Markers and recreating the Rich Content. Sounds simple right?   I've been working on this for about two weeks now and am making progress.  I believe this is simple (just like everything) once you figure out way more than you ever wanted to learn concerning Rich Text Box internals, all because we can't get access to the raw Byte[] of a RTB.

     

     

     

     

     

     

     

     

     

     

     

     

    Thursday, April 14, 2011 10:03 AM
  • I would rather writing a custom RTB from scratch, and that's where I stopped. Too much work....

    Another problem with the built-in RTB is that the inline styles take too much space. In general, more than half of the content is from the styles, and the same styles are just repeated again and again...

    Thursday, April 14, 2011 10:20 AM
  • I would rather writing a custom RTB from scratch, and that's where I stopped.

    Yah, that's really beyond the scope of a single person.

    and the same styles are just repeated again and again...

    If Microsoft actually ate their own dogfood, perhaps it would force them to address issues like this. The RichTextBox in Silverlight is almost completely useless because:

    Serialization is a pain

    Can't really bind the thing

    There's no equivalent to things like tinyMCE.

    Thursday, April 14, 2011 12:31 PM
  • Hey JackBond;

      How would we implement TinyMCE into a Silverlight application?  Any ideas?

    Thursday, April 14, 2011 1:06 PM
  • How would we implement TinyMCE into a Silverlight application?  Any ideas?

    Something like that should be in the Silverlight toolkit. As for implementing it, I don't think it would be rocket science, just a ton of grunt work.

    Thursday, April 14, 2011 1:39 PM
  • Ok just looked into the design of TinyMCE a bit, it looks to be Javascript all the way. 

    Look at this the GetContent mode support raw.   So.... if we backend with a RIA service and save the byte[] we've done it...

    Thursday, April 14, 2011 11:32 PM
  • This works over in WPF land...

    private MemoryStream SaveRtb()
    {
        var textRange = new TextRange(richTextBox1.Document.ContentStart, richTextBox1.Document.ContentEnd);
        var ms = new MemoryStream();
        textRange.Save(ms, System.Windows.DataFormats.XamlPackage);
        ms.Close();
        return ms;
    }
    
    private static string ConvertToString(MemoryStream ms)
    {
        string str = Encoding.Default.GetString(ms.ToArray());
        return str;
    }
    
    private void PopulateRtb2(MemoryStream ms)
    {
        var flowDocument = new FlowDocument();
        var textRange2 = new TextRange(flowDocument.ContentStart, flowDocument.ContentEnd);
        textRange2.Load(ms, DataFormats.XamlPackage);
        richtextbox2.Document = flowDocument;
    }

    How to save Images in a RTB!

    JP Cowboy Coders Unite!

    Wednesday, January 2, 2013 4:16 AM