How to add data into RichTextBox from two datasourses in WPF - wpf

I need to put data from two different datasourses in the same textbox. The text that comes from the first one have to be bolded and the secound normal.
It's there a possibility to do this in WPF?

You cannot bind (or multibind) to Document property of RichTextBox, because it is NOT a DependencyProperty (strange!!!)!!! See this link for a really easy way of subclassing RichTextBox to create your own BindableRichTextBox or this post for another workaround.
Now you can use MultiBinding with a custom IMultiValueConverter to achieve the results. Since you have not given much details of your problem, I can only give you an overall idea of what you should do:
<!--NOTE: Include xmlns:local=" .. " appropriately for your project-->
<Window.Resources>
<sys:String x:Key="SourceA">This text will be normal..</sys:String>
<sys:String x:Key="SourceB">This text will be Bold!!!</sys:String>
</Window.Resources>
And now you can do like this:
<local:BindableRichTextBox>
<!--<local:BindableRichTextBox.Document>-->
<MultiBinding Converter="{x:Static local:MySourceBToBoldConverter.Instance}">
<Binding Source="{StaticResource SourceA}" />
<Binding Source="{StaticResource SourceB}" />
</MultiBinding>
<!--</local:BindableRichTextBox.Document>-->
</local:BindableRichTextBox>
And then create a class MySourceBToBoldConverter that inherits from IMultiValueConverter like this:
public class MySourceBToBoldConverter : IMultiValueConverter
{
public static readonly MySourceBToBoldConverter Instance = new MySourceBToBoldConverter();
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//Now you'll get value from Source A as value[0]
// and value from Source B as value[1]
//Do whatever you want like bold etc...
//and return the result
string normalText = values[0] as string;
string boldText = values[1] as string;
Bold bold = new Bold();
bold.Inlines.Add(boldText);
Paragraph para = new Paragraph();
para.Inlines.Add(normalText);
para.Inlines.Add(bold);
FlowDocument rtbDocument = new FlowDocument();
rtbDocument.Blocks.Add(para);
return rtbDocument;
}
public object[] ConvertBack(object value, ... )
{
//Convert the object returned by Convert() back
//to its original form if it's possible;
//otherwise throw not supported exception ;)
throw new NotImplementedException();
}
}
Currently I don't have my work PC with me that has VS installed, so I can't give you a working example, but go ahead and search google/msdn/stackoverflow 4 MultiBinding and IMultiValueConverter and you'll find some good examples out there.
Check the working example here.
Regards,
Mihir Gokani

Related

Is it possible to concatenate an imagesourece Uri in xaml

Hi I've been looking at ways to dynamically change an image Uri in xaml and in my research came across the following answer, which has certainly given me hope that what I really want to do might just be possible. In the original question the questioner wanted to swap the image itself, in my case I want to swap the directory where the image is located.
So when one looks at the answer that #Clemens provided one ends up with an images source being bound to a dependency property that is dynamically set when the form loads.
What I'd like to know is whether it would be feasible to set the location part of the uri dynamically (as per the logic that #Clemens is advocating and then simply append the Image name in the actual binding statement so that it might look something like this:
<Image Source="{Binding ImageUri & myImage.png}"/>
Essentially I have a number of buttons to which I would like to assign a default image og a size to be determined by the end user. To that end the Images would be stored in different folders in the application (in fact its a custom control) and then the relevant path bit of the URI would be set as per the suggestion in the referenced answer and I'd just append the Image name (which would be the same for the button irrespective of the size) and it would then have the correct image to display.
MainWindow.xaml.cs :
namespace MainWindowNamespace
{
public sealed class ImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
string fullPath = Path.GetFullPath((string)value);
return new BitmapImage(new Uri(fullPath));
}
catch { return null; }
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{ throw new NotImplementedException(); }
}
}
MainWindow.xaml :
<Window
xmlns:imgConvert="clr-namespace:MainWindowNamespace">
<Window.Resources>
<imgConvert:ImageConverter x:Key="ImageConverter" />
</Window.Resources>
<Image ImageSource="{Binding ImagePath, Converter={StaticResource ImageConverter}}" />
</Window>

Using resources as conversion results in a bind converter

When I try to bind a valueconverter from a defined enum Status to brush, I get an error in my XAML designer:
'OKStatus' resource not found.
The application works fine runtime, but I'm not able to see my GUI in the designer.
My resources are defined in the color.xaml file, which is read at run time.
All code is within the same namespace
My XAML:
xmlns:config="clr-namespace:App.MyNamespace"
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="c:\Skins\Colors.xaml" />
<ResourceDictionary Source="c:\Skins\Common.xaml" />
</ResourceDictionary.MergedDictionaries>
<config:StatusConverter x:Key="StateConverter" />
<config:BoolConverter x:Key="BoolConverter" />
<config:BooleanConverter x:Key="BooleanConverter" />
</ResourceDictionary>
</UserControl.Resources>
and
Status
My converter:
[ValueConversion(typeof(bool), typeof(Brush))]
public class BoolConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
bool state = (bool)value;
FrameworkElement FrameElem = new FrameworkElement();
if (state == true)
return (FrameElem.FindResource("OKStatus") as Brush);
else
return (FrameElem.FindResource("ErrorStatus") as Brush);
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return null;
}
}
In this code the frameElem wont have any knowledge of the resources I have defined I guess, so I need a way to get access to my resources during design.
Is this possible?
Yes, it is possible, and your guess is correct. Resource finding starts with the logical tree, and creating a new FrameworkElement() doesn't satisfy this. It's completely disconnected.
What you can do (and what you may have to do if N8's suggestion doesn't work), is to hand your converter a reference to the UserControl as the FrameworkElement to call FindResource() on.
The reason N8's suggestion probably won't work is that Application.Current.FindResource() probably starts at application-level resources and then goes to system resources, but the resources you're after are in the UserControl's resources. If they were placed in App.xaml's resources, it would work. However, I think Application.Current may be null at design-time.
The easiest way I can think of to do this is in your UserControl's constructor:
public MyUserControl(){
var boolconv = new BoolConverter();
boolconv.FrameworkElement = this;
this.Resources.Add( "BoolConverter", boolconv );
InitializeComponent();
}
I'm pretty sure it goes before InitializeComponent(), rather than after.
Doing this in XAML would be more complicated, as you probably have to add a DependencyProperty to your converter so that you could bind the UserControl to it. I think that would be going overboard.
Another way is to put TrueBrush and FalseBrush properties on your converter and assign them in XAML, which is what I tend to do so that my converters are vague and general-use. (NB: Names are slightly different.)
<config:BoolToBrushConverter x:Key="Bool2Brush"
TrueBrush="{StaticResource OKStatusBrush}"
FalseBrush="{StaticResource ErrorStatusBrush}" />
I think the issue is that you are trying to find the resource out of a framework element not in the visual tree. Could you try the following instead?
Application.Current.FindResource("OKStatus") as Brush;
As I have learned by TechNet Wiki, there is necessary to use MultiValue Converter and MultiValueBinding to get correct registred converter and correct FrameworkElement by the UserControl.
XAML Example:
<TextBlock x:Name="tb1" Margin="20">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource MyConverter}">
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type UserControl}}"/>
<Binding Path="MyValue"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Then the converter declaration can looks :
public class MyConverter : IMultiValueConverter
{
FrameworkElement myControl;
object theValue;
public object Convert(object[] values, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
myControl = values[0] as FrameworkElement;
theValue = values[1];
return myControl.FindResource(">>resource u need<<");
}
public object[] ConvertBack(object value, System.Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
.....
}
}
The detail explanation is:
https://social.technet.microsoft.com/wiki/contents/articles/12423.wpfhowto-pass-and-use-a-control-in-it-s-own-valueconverter-for-convertconvertback.aspx
I have run into this problem as well. I think that calling Application.Current is the best way to get at resources from an IValueConverter, since they are not defined on a per window, or page or control, level. This would require the resources to be at least application-level, as stated above.
However, since the Application.Current reference is set to null in the designer, this method will always break the designer. What you appear to have done is given something for the designer to display, while you have given your running application access to resources in the converter.
For all of you out there with this issue, you don't need to implement the Kludge that lewi implemented; this is only if you want the designer surface to load. It does not affect your application while running as the Application.Current call has something to do.
Actually what I ended up doing (for now) was to change from FindResource to TryFindResource, and put the statements in a try/catch block.
This seems to work so far.
try
{
if (state == true)
return (FrameElem.TryFindResource("OKStatus") as Brush);
else
return (FrameElem.TryFindResource("ErrorStatus") as Brush);
}
catch (ResourceReferenceKeyNotFoundException)
{
return new SolidColorBrush(Colors.LightGray);
}

wpf xaml calling a method on the current object

Im attempting to bind to the output of a method. Now I've seen examples of this using ObjectDataProvider However the problem with this is ObjectDataProvider creates a new instance of the object to call the method. Where I need the method called on the current object instance. I'm currently trying to get a converter to work.
Setup:
Class Entity
{
private Dictionary<String, Object> properties;
public object getProperty(string property)
{
//error checking and what not performed here
return this.properties[property];
}
}
My attempt at the XAML
<local:PropertyConverter x:Key="myPropertyConverter"/>
<TextBlock Name="textBox2">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource myPropertyConverter}"
ConverterParameter="Image" >
<Binding Path="RelativeSource.Self" /> <!--this doesnt work-->
</MultiBinding>
</TextBlock.Text>
</TextBlock>
my code behind
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
string param = (string)parameter;
var methodInfo = values[0].GetType().GetMethod("getProperty", new Type[0]);
if (methodInfo == null)
return null;
return methodInfo.Invoke(values[0], new string[] { param });
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException("PropertyConverter can only be used for one way conversion.");
}
My problem is that I cant seem to pass the current Entity into the converter. So When i try to use reflection to get the getProperty method I have nothing to operate on
thanks, steph
Wrap the call to the method inside a get property and add this get property to whatever class that is your current DataContext.
Edit: Answering your updated question.
If you only pass one parameter to the valueconverter you don't need a multivalueconverter, just use a regular valueconverter (implementing IValueConverter). Also, why not cast the object in the valueconverter to a Distionary and use it directly instead of using reflection.
To pass current datacontext as a binding do this: <Binding . />. I'm guessing the datacontext of the textblock is entity.
Still, all this is not necessary if all you want to do is run some code before accessing a dictionary item. Just use an index property instead, you can databind to it directly:
public class Entity
{
private Dictionary<String, Object> properties;
public object this[string property]
{
get
{
//error checking and what not performed here
return properties[property];
}
}
}
<TextBlock Text="{Binding Path=[Image]}" />

WPF - Dynamically access a specific item of a collection in XAML

I have a data source ('SampleAppearanceDefinitions'), which holds a single collection ('Definitions'). Each item in the collection has several properties, including Color, which is what I'm interested in here.
I want, in XAML, to display the Color of a particular item in the collection as text. I can do this just fine using this code below...
Text="{Binding Source={StaticResource SampleAppearanceDefinitions}, Path=Definitions[0].Color}"
The only problem is, this requires me to hard-code the index of the item in the Definitions collection (I've used 0 in the example above). What I want to do in fact is to get that value from a property in my current DataContext ('AppearanceID'). One might imagine the correct code to look like this....
Text="{Binding Source={StaticResource SampleAppearanceDefinitions}, Path=Definitions[{Binding AppearanceID}].Color}"
...but of course, this is wrong.
Can anyone tell me what the correct way to do this is? Is it possible in XAML only? It feels like it ought to be, but I can't work out or find how to do it.
Any help would be greatly appreciated!
Thanks!
AT
MultiBinding is your friend here:
Assuming you have a TextBlock:
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource AppearanceIDConverter}">
<Binding Source="{StaticResource SampleAppearanceDefinitions}" />
<Binding Path="AppearanceID" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
And define a MultiValueConverter to return what you wish to see:
public class AppearanceIDConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
List<item> items = (List<item>)values[0]; //Assuming its items in a List
int id = (int)values[1]; //Assuming AppearanceID is an integer
return items.First(i => i.ID == id).Color; //Select your item based on the appearanceID.. I used LINQ, but a foreach will work just fine as well
}
public object[] ConvertBack(object value, System.Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new System.NotImplementedException();
}
#endregion
}
Of course, you will need to set the converter as a resource in your Resource dictionary, like you did SampleAppearanceDefinitions. You can also ditch the multibinding and use a regular binding to AppearanceID with a IValueConverter, if you can get to the SampleAppearanceDefinitions collection through code ;).
Hope this helps
Even if it could be possible you'd better not do that this way, but instead use a dedicated property in your view model or in the code behind of your view if it has only a pure graphical meaning.
This property, say "CurrentAppearance", would expose a Color property you could bind from your Xaml :
Text="{Binding CurrentAppearance.Color}"
which is more understandable.
As a general advice : avoid to spoil your Xaml with plumbing code : Xaml should be as readable as possible,
particularly if you work with a team of designers that have no coding skills and do not want to be concerned with the way you are managing the data.
Moreover, if later you decide to change the way data are managed you would not have to change your Xaml.
MultiBinding might actually work if your list is on a viewmodel instead of a staticresource. I was suprised myself to see that the object passed on to the view is actually a pointer to the object on the model, so changing the object in the view (eg. typing in new test in the textbox) directly affects the model object.
This worked for me. The ConvertBack method is never useed.
public class PropertyIdToPropertyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length == 2)
{
var properties = values[0] as ObservableCollection<PropertyModel>;
if (properties != null)
{
var id = (int)values[1];
return properties.Where(model => model.Id == id).FirstOrDefault();
}
}
return null;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Using file-path to images stored within the Application Settings

I am trying to develop an application that uses a number of images that are stored in a seperate remote file location. The file-paths to the UI elements are stored within the Application Settings. Although I understand how to access and use the file-path from Settings in C# (Properties.Settings.Default.pathToGridImages + "OK.png"), I am at a loss to figure out how to utilize the Settings paths in WPF, and can only seem to access the file if I include the file-path, such as:
<Grid.Background>
<ImageBrush ImageSource="C:\Skins\bottomfill.png" TileMode="Tile" />
</Grid.Background>
I would have thought that concatenating "Properties.Settings.Default.pathToGridImages" with "bottomfill.png" in WPF could be done much like it can be done in C#. Can anyone please point me in the right direction?
You can do this using a MultiBinding and a value converter. To start with, use a multibinding to bind your image source to the base path, and the image name:
<ImageBrush>
<ImageBrush.ImageSource>
<MultiBinding Converter="{StaticResource MyConverter}">
<Binding Source="{StaticResource MySettings}" Path="Default.FilePath" />
<Binding Source="ImageName.png"></Binding>
</MultiBinding>
</ImageBrush.ImageSource>
</ImageBrush>
You then need to have a converter that implements IMultiValueConverter and combines the two parts of the path and creates the image using either an ImageSourceConverter or by creating a new BitmapImage:
class MyConverter: IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
// Concatenate the values.
string filename = Path.Combine(values[0].ToString(), values[1].ToString());
// You can either use an ImageSourceConverter
// to create your image source from the path.
ImageSourceConverter imageConverter = new ImageSourceConverter();
return imageConverter.ConvertFromString(filename);
// ...or you can create a new bitmap with the combined path.
return new BitmapImage(new Uri(filename, UriKind.RelativeOrAbsolute));
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
// No need to implement the convert back as this will never be used in two way binding.
throw new NotImplementedException();
}
}
Obviously, you need to declare namespaces and resource to the CLR stuff in the XAML so you can access it (If you've called your settings and converter classes something different, make sure you change this to match up):
...
xmlns:local ="clr-namespace:WpfApplication1">
<Window.Resources>
<local:MyConverter x:Key="MyConverter"></local:MyConverter>
<local:MySettings x:Key="MySettings"></local:MySettings>
</Window.Resources>
I've tested it out and it works fine.
[An alternative way would be just to bind the ImageSource property to a property on your data context that combined the paths in C# code, but that would depend on how you've got your datacontexts set up, so may be undesirable in many cases.]

Resources