WP7 - How to bind the Bitmap from ViewModel? - c#

I have bit map in View Model. Now I want to bind to the XAML from View Model.
public static String _imgQRCode;
public String imgQRCode
{
get { return _imgQRCode; }
set { this.RaiseAndSetIfChanged(x => x.imgQRCode, value); }
}
Bit Map:-
imgQRCode = GenerateQRCode(phoneNumber).ToString();
GenerateORCode:-
private static WriteableBitmap GenerateQRCode(string phoneNumber)
{
BarcodeWriter _writer = new BarcodeWriter();
_writer.Renderer = new ZXing.Rendering.WriteableBitmapRenderer()
{
Foreground = System.Windows.Media.Color.FromArgb(255, 0, 0, 255),
};
_writer.Format = BarcodeFormat.QR_CODE;
_writer.Options.Height = 400;
_writer.Options.Width = 400;
_writer.Options.Margin = 1;
var barcodeImage = _writer.Write("tel:" + phoneNumber);
return barcodeImage;
}
Here i can not bind the image. Please let me any idea to bind the image from viewModel.
Thanks in advance.

Change the data type from string to WriteableBitmap for imgQRCode.

You can not bind the image to string.
So long as datacontext is correct for your tag, you should define a property for the bitmap, e.g.
WriteableBitmap QRCode { get; set; } // Implement INotifyPropertyChanged the way you do it
Then have QRCode set in your other property setter, like so:
public String imgQRCode
{
get { return _imgQRCode; }
set
{
this.RaiseAndSetIfChanged(x => x.imgQRCode, value);
this.QRCode = GenerateQRCode(value);
}
}
then in XAML you can do <Image Source="{Binding Path=QRCode}" />

Related

UWP: Compute text height in a RichTextBlock gives weird results

I need a reliable method to get the height of the text contained in a RichTextBlock, even before it is actually drawn on the scene.
Using the normal Measure() method produces a weird result, as it can be seen in the MVCE: https://github.com/cghersi/UWPExamples/tree/master/MeasureText (I want to keep fiexed the width, and measure the final height, but the result of DesiredSize is far different from the actual height!!).
For this reason, I found a rough method (mentioned here https://stackoverflow.com/a/45937298/919700), that I extended to serve my purpose, where we use some Win2D API to compute the content height.
The problem is that in some cases, this method provides an height that is smaller than the expected one.
Is there a general way to retrieve the (correct) height of a
TextBlock, even before it is drawn on the scene?
If this is not the case, what am I doing wrong?
Here's my code (which you can find also as MVCE here: https://github.com/cghersi/UWPExamples/tree/master/RichText):
public sealed partial class MainPage
{
public static readonly FontFamily FONT_FAMILY = new FontFamily("Assets/paltn.ttf#Palatino-Roman");
public const int FONT_SIZE = 10;
private readonly Dictionary<string, object> FONT = new Dictionary<string, object>
{
{ AttrString.FONT_FAMILY_KEY, FONT_FAMILY },
{ AttrString.FONT_SIZE_KEY, FONT_SIZE },
{ AttrString.LINE_HEAD_INDENT_KEY, 10 },
{ AttrString.LINE_SPACING_KEY, 1.08 },
{ AttrString.FOREGROUND_COLOR_KEY, new SolidColorBrush(Colors.Black) }
};
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
private readonly RichTextBlock m_displayedText;
public MainPage()
{
InitializeComponent();
// create the text block:
m_displayedText = new RichTextBlock
{
MaxLines = 0, //Let it use as many lines as it wants
TextWrapping = TextWrapping.Wrap,
AllowFocusOnInteraction = false,
IsHitTestVisible = false,
Width = 80,
Height = 30,
Margin = new Thickness(100)
};
// set the content with the right properties:
AttrString content = new AttrString("Excerpt1 InkLink", FONT);
SetRichText(m_displayedText, content);
// add to the main panel:
MainPanel.Children.Add(m_displayedText);
// compute the text height: (this gives the wrong answer!!):
double textH = GetRichTextHeight(content, (float)m_displayedText.Width);
Console.WriteLine("text height: {0}", textH);
}
public static double GetRichTextHeight(AttrString text, float maxWidth)
{
if (text == null)
return 0;
CanvasDevice device = CanvasDevice.GetSharedDevice();
double finalH = 0;
foreach (AttributedToken textToken in text.Tokens)
{
CanvasTextFormat frmt = new CanvasTextFormat()
{
Direction = CanvasTextDirection.LeftToRightThenTopToBottom,
FontFamily = textToken.Get(AttrString.FONT_FAMILY_KEY, FONT_FAMILY).Source,
FontSize = textToken.Get(AttrString.FONT_SIZE_KEY, FONT_SIZE),
WordWrapping = CanvasWordWrapping.Wrap
};
CanvasTextLayout layout = new CanvasTextLayout(device, textToken.Text, frmt, maxWidth, 0f);
finalH += layout.LayoutBounds.Height;
}
return finalH;
//return textBlock.Blocks.Sum(block => block.LineHeight);
}
private static void SetRichText(RichTextBlock label, AttrString str)
{
if ((str == null) || (label == null))
return;
label.Blocks.Clear();
foreach (AttributedToken token in str.Tokens)
{
Paragraph paragraph = new Paragraph()
{
TextAlignment = token.Get(AttrString.TEXT_ALIGN_KEY, TextAlignment.Left),
TextIndent = token.Get(AttrString.LINE_HEAD_INDENT_KEY, 0),
};
double fontSize = token.Get(AttrString.FONT_SIZE_KEY, FONT_SIZE);
double lineSpacing = token.Get(AttrString.LINE_SPACING_KEY, 1.0);
paragraph.LineHeight = fontSize * lineSpacing;
paragraph.LineStackingStrategy = LineStackingStrategy.BlockLineHeight;
Run run = new Run
{
Text = token.Text,
FontFamily = token.Get(AttrString.FONT_FAMILY_KEY, FONT_FAMILY),
FontSize = fontSize,
Foreground = token.Get(AttrString.FOREGROUND_COLOR_KEY, new SolidColorBrush(Colors.Black)),
FontStyle = token.Get(AttrString.ITALIC_KEY, false) ?
Windows.UI.Text.FontStyle.Italic : Windows.UI.Text.FontStyle.Normal
};
paragraph.Inlines.Add(run);
label.Blocks.Add(paragraph);
}
}
}
public class AttrString
{
public const string FONT_FAMILY_KEY = "Fam";
public const string FONT_SIZE_KEY = "Size";
public const string LINE_HEAD_INDENT_KEY = "LhI";
public const string LINE_SPACING_KEY = "LSpace";
public const string FOREGROUND_COLOR_KEY = "Color";
public const string ITALIC_KEY = "Ita";
public const string TEXT_ALIGN_KEY = "Align";
public const string LINE_BREAK_MODE_KEY = "LineBreak";
public static Dictionary<string, object> DefaultCitationFont { get; set; }
public static Dictionary<string, object> DefaultFont { get; set; }
public List<AttributedToken> Tokens { get; set; }
public AttrString(string text, Dictionary<string, object> attributes)
{
Tokens = new List<AttributedToken>();
Append(text, attributes);
}
public AttrString(AttrString copy)
{
if (copy?.Tokens == null)
return;
Tokens = new List<AttributedToken>(copy.Tokens);
}
public AttrString Append(string text, Dictionary<string, object> attributes)
{
Tokens.Add(new AttributedToken(text, attributes));
return this;
}
public bool IsEmpty()
{
foreach (AttributedToken t in Tokens)
{
if (!string.IsNullOrEmpty(t.Text))
return false;
}
return true;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
foreach (AttributedToken t in Tokens)
{
sb.Append(t.Text);
}
return sb.ToString();
}
}
public class AttributedToken
{
public string Text { get; set; }
public Dictionary<string, object> Attributes { get; set; }
public AttributedToken(string text, Dictionary<string, object> attributes)
{
Text = text;
Attributes = attributes;
}
public T Get<T>(string key, T defaultValue)
{
if (string.IsNullOrEmpty(key) || (Attributes == null))
return defaultValue;
if (Attributes.ContainsKey(key))
return (T)Attributes[key];
else
return defaultValue;
}
public override string ToString()
{
return Text;
}
}
** UPDATE **:
After further digging into the issue, the problem seems related to the lack of configurability for the CanvasTextFormat object, especially for the indentation of the first line (expressed in the RichTextBlock using the property Paragraph.TextIndent). Is there any way to specify such setting in a CanvasTextFormat object?
Looking at your MeasureText MVCE code, the problem with calling Measure() on the RichTextBlock comes down to this line:
m_textBlock.Margin = new Thickness(200);
This sets a universal margin of 200 on all sides, which means the element needs at least 200 width on the left plus 200 width on the right, or 400 width. Since your Measure(300,infinite) specifies an available width of less than the minimum required 400 width, the RichTextBlock decides that the best it can do is wrap the text at every character, producing the massive 5740 pixel height (plus the 200+200 height from the margin).
If you remove that line, the RichTextBlock will use the specified constraint of 300 and correctly measure its desired height as 90 pixels, which is what it renders as on screen (if you set Width=300 or otherwise result in the actual element layout to have the same constraint).
Alternatively, since you know the width you want for the element, you could set Width=300 on it and it will then measure with that width. The Height will be expanded as a result of the set Margin, though.
I'm assuming you don't actually have Margin=200 set in your real app, and instead have something smaller like Margin=5 to account for margin you actually want when the RichTextBlock is in the tree and drawing. If this is the case, then you can either:
Use the Width=300 approach for measuring and subtract off the top+bottom margin from the DesireSize.Height.
Measure with (300+margin.Left+margin.Right) as the width so that once the margin is subtracted off from that total availableSize the remaining width the text can use is your intended 300. You'll still need to subtract off the top+bottom margin from the DesireSize.Height.

Xamarin forms convert image uri address to real image

I am trying to solve uri address converting to image issue. Main idea , what I am doing I want to pick image from a gallery, bind it and save it to database. Everything is working, I can save string image path to class property, but unfortunately I can't convert that address to my imageSource where I will displaying my image, because now I see empty image circle.
This is where I am selecting image from gallery and trying to convert into image:
IGalleryImageService galleryService = Xamarin.Forms.DependencyService.Get<IGalleryImageService>();
galleryService.ImageSelected += (o, imageSourceEventArgs) =>
{
Uri uri = new Uri(imageSourceEventArgs.ImageSource);
(ActivePage.Page as PageTemplate).CarImage.Source = ImageSource.FromFile(uri.ToString());
ActivePage.CarImageBindable = (ActivePage.Page as PageTemplate).CarImage.Source.GetValue(StreamImageSource.StreamProperty).ToString(); // here I am trying to convert from path address to image
};
galleryService.SelectImage();
Here is my PageTemplate
public partial class PageTemplate: ContentPage
{
public CircleImage CarImage
{
get
{
return Car;
}
set
{
Car = value;
}
}
}
and PageTemplate.xaml where I am displaying images.
<controls:CircleImage x:Name="Car" AbsoluteLayout.LayoutBounds=".5,0,-1,-1" AbsoluteLayout.LayoutFlags="PositionProportional" Aspect="AspectFill">
</controls:CircleImage>
This is my bindable property from Unit2 class:
public string CarImageBindable
{
get
{
return base.CarImage;
}
set
{
base.CarImage = value;
OnPropertyChanged(nameof(CarImageBindable));
}
}
And another property from Core project Unit class:
public int Id { get; set; }
public DateTime StartDate { get; set; }
public string CarImage { get; set; }
That's why I decided to make all properties as string data type, because I want to save image path. And yes, then convert again from database to physical image.
Thank you for answers or suggestions.
Well, I solved issue like this:
IGalleryImageService galleryService = Xamarin.Forms.DependencyService.Get<IGalleryImageService>();
galleryService.ImageSelected += (o, imageSourceEventArgs) =>
{
ActiveParking.CarImageBindable = imageSourceEventArgs.ImageSource.ToString();
(ActiveParking.Page as PageTemplate).CarImage.Source = galleryService.GetImage(imageSourceEventArgs.ImageSource.ToString());
};
galleryService.SelectImage();

Binding Command/Attribute To Element in C#?

I'm trying to implement the XLabs CameraViewModel functionality into my Xamarin Forms App. Unfortunately, the given example uses XAML to bind the views with data, but i need to do it in code behind.
The following code is used to select a picture and get it's source.
public class CameraViewModel : XLabs.Forms.Mvvm.ViewModel
{
...
private ImageSource _imageSource;
private Command _selectPictureCommand;
public ImageSource ImageSource
{
get { return _imageSource; }
set { SetProperty(ref _imageSource, value); }
}
public Command SelectPictureCommand
{
get
{
return _selectPictureCommand ?? (_selectPictureCommand = new Command(
async () => await SelectPicture(),() => true));
}
}
...
}
And these commands are bound to XAML :
<Button Text="Select Image" Command="{Binding SelectPictureCommand}" />
<Image Source="{Binding ImageSource}" VerticalOptions="CenterAndExpand" />
How can I apply the same commands in code-behind for created elements?
CameraViewModel ViewModel = new CameraViewModel();
var Take_Button = new Button{ };
Take_Button.SetBindings(Button.CommandProperty, //*???*//);
var Source_Image = new Image { };
Source_Image.SetBinding(Image.SourceProperty, //*???*//);
I've successfully binded SelectPictureCommand by doing the following:
Take_Button .Command = ViewModel.SelectPictureCommand;
However I have my doubts about it being the correct way, and the same logic cannot be applies to ImageSource.
For the button you have:
var Take_Button = new Button{ };
Take_Button.SetBinding(Button.CommandProperty, new Binding { Path = nameof(ViewModel.SelectPictureCommand), Mode = BindingMode.TwoWay, Source = ViewModel});
For the image you have:
var Source_Image = new Image { };
Source_Image.SetBinding(Image.SourceProperty, new Binding { Path = nameof(ViewModel.ImageSource), Mode = BindingMode.TwoWay, Source = ViewModel });

XAML List<LineSeries> binding to chart Series

I have MVVM silverlight app with toolkit charts.
In view model I created ObservableCollection property:
private ObservableCollection<LineSeries> _lines = new ObservableCollection<LineSeries>();
public ObservableCollection<LineSeries> Lines
{
get { return _lines; }
set
{
_lines = value;
NotifyPropertyChanged("Lines");
}
}
Then in some method I populate this collection with dynamic count lines:
List<SolidColorBrush> colors = BS2ColorSetHelper.GetSetColors();
for (int i = 0; i < remainderData.Count; i++)
{
LineSeries line = (colors.ElementAtOrDefault(i) != null)
? CreateNewLineSeriesWithColor(remainderData[i].DenominationName, remainderData[i].Coords, colors[i])
: CreateNewLineSeries(remainderData[i].DenominationName, remainderData[i].Coords);
line.Name = remainderData[i].DenominationName;
Lines.Add(line);
}
.........
Now I want to bind this ObservableCollection to toolkit chart series.
<toolkit:Chart Name="chart">
<toolkit:Chart.Series>
????
</toolkit:Chart.Series>
</toolkit:Chart>
I have tried
Series="{Binding Path=Lines}"
but it doesn't work. Visual Studio shows an error: Object of type 'System.Windows.Data.Binding' cannot be converted to type 'System.Collections.ObjectModel.Collection`1[System.Windows.Controls.DataVisuali‌​zation.Charting.ISeries]'. I think it's because Series are not dependency property.
Ok, we can't bind LineSeries to Series because Series are not Dependency property.
So we can create new UserControl with this dependency properties:
public class MultiChart : Chart
{
public IEnumerable SeriesSource
{
get
{
return (IEnumerable)GetValue(SeriesSourceProperty);
}
set
{
SetValue(SeriesSourceProperty, value);
}
}
public static readonly DependencyProperty SeriesSourceProperty = DependencyProperty.Register(
name: "SeriesSource",
propertyType: typeof(IEnumerable),
ownerType: typeof(MultiChart),
typeMetadata: new PropertyMetadata(
defaultValue: default(IEnumerable),
propertyChangedCallback: new PropertyChangedCallback(OnSeriesSourceChanged)
)
);
private static void OnSeriesSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
IEnumerable newValue = (IEnumerable)e.NewValue;
MultiChart source = (MultiChart)d;
source.Series.Clear();
foreach (LineSeries item in newValue)
{
source.Series.Add(item);
}
}
}
Then we just bind LineSeries to newly created property:
<common:MultiChart Name="chart"
Title="{Binding Path=Title}"
SeriesSource="{Binding Path=Lines}" />
The View Model will be:
public class ChartDenominationViewModel : ViewModel
{
private string _title;
public string Title
{
get { return _title; }
set
{
_title = value;
NotifyPropertyChanged("Title");
}
}
private ObservableCollection<LineSeries> _lines = new ObservableCollection<LineSeries>();
public ObservableCollection<LineSeries> Lines
{
get { return _lines; }
set
{
_lines = value;
NotifyPropertyChanged("Lines");
}
}
}
I don't know fully the information about the control Chart I would suggest you check the documentation provided by the control originator to find out the name of the bindable property.
But from what you post my guess would be to try:
<toolkit:Chart Name="chart" Series={Binding Lines}/>
guessing that your Datacontext is all in place. meaning the datacontext of the page is set to your viewmodel

Passing a list that includes a nested list of datetimes on command execution not working in wpf

I have a rather strange requirement for a Wpf Project I'm working on. I want to be able to build a XamDataGrid with a series of DateTime fields when the user saves the data from another grid. Currently I see the second XamDataGrid with it's fields, but upon execution of the command that saves the data, although I can see in the debugger that my second list (which is bound to the second XamDataGrid) is generated, nothing displays on this second XamDataGrid.
I'll post most of my code so that somebody might help me:
The xaml (for the second datagrid as the first one is working fine):
<igDP:XamDataGrid.FieldLayouts>
<igDP:FieldLayout>
<igDP:Field Label="ID" Name="id" Width="50"></igDP:Field>
<igDP:Field Label="Descripcion" Name="descripcion" Width="400"></igDP:Field>
<igDP:UnboundField Label="Fechas de Pago" Name="cadaPago" Width="400">
</igDP:UnboundField>
<igDP:Field Label="Colores" Name="Colores" Visibility="Collapsed" />
</igDP:FieldLayout>
</igDP:XamDataGrid.FieldLayouts>
</igDP:XamDataGrid>
`
The code in my viewmodel for the second grid:
public List<ClaseFechasPago> ListaFechasPago
{
get { return listaFechasPago; }
set { listaFechasPago = value; notifyChanges("ListaFechasPago"); }
}
public void PintarFechas(List<ClaseFechasPago> f)
{
ListaFechasPago.Clear();
foreach (ClaseFechasPago fecha in f)
{
fecha.cadaPago = new List<DateTime>();
for (int i = 0; i < fecha.numPagos; i++)
{
fecha.cadaPago.Add(new DateTime());
}
ListaFechasPago.Add(fecha);
}
}
public vmCursos_y_Diplomados()
{
Comando = new cmdCursos_y_Diplomados();
Comando.ViewModel = this;
ListaCursosyDiplomados = new List<ClaseCursosyDiplomados>();
ListaFechasPago = new List<ClaseFechasPago>();
this.cargarDatos();
this.PintarFechas(ListaFechasPago);
}
Now on the command I'm doing the following
public void Execute(object parameter)
{
List<CatEntidadesEducacionContinua> cursos = new List<CatEntidadesEducacionContinua>();
List<ClaseFechasPago> fechas = new List<ClaseFechasPago>();
foreach (ClaseCursosyDiplomados C in ViewModel.ListaCursosyDiplomados.Where(t=>t.Colores==1).ToList())
{
cursos.Add(new CatEntidadesEducacionContinua
{
IdEntidadEducacionContinua = C.id, Coordinador=C.coordinador, Descripcion=C.descripcion, FechaUltimoCambio = DateTime.Now,
FechaInicio = C.fechaInicio, FechaTermino=C.fechaTermino, Precio=C.precio, NumeroDePagos=C.numeroDePagos, FechasPagos=C.fechasPagos, Inhabilitado=C.inhabilitado,
});
if (C.numeroDePagos > 1)
{
ClaseFechasPago f = new ClaseFechasPago();
f.numPagos = C.numeroDePagos;
f.descripcion = C.descripcion;
f.id = C.id;
fechas.Add(f);
}
}
System.Windows.MessageBox.Show(new Entidades.MetodoCursos_y_Diplomados().SetEntidadEContinua(cursos), "Entidades de Educación Continua", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Information);
//System.Windows.MessageBox.Show(new Entidades.MetodoFechasPago().pintarFechasPago
ViewModel.cargarDatos();
ViewModel.PintarFechas(fechas);
}
But as I said it's not working, the execution results in the following screenshot, where the second grid is not populated:
Oh and I also forgot earlier to show the code for my custom class, out of which the list bound to the XamDataGrid is made of:
public class ClaseFechasPago : Utils.PropertyChange
{
private List<DateTime> _cadaPago;
public List<DateTime> cadaPago
{
get { return _cadaPago; }
set
{
_cadaPago = value;
if (EntroPrimeraVez)
{
Colores = 1;
}
}
}
private int? _numPagos;
public int? numPagos
{
get { return _numPagos; }
set
{
_numPagos = value;
if (EntroPrimeraVez)
{
Colores = 1;
}
}
}
private int _id;
public int id
{
get { return _id; }
set
{
_id = value;
}
}
private string _descripcion;
public string descripcion
{
get { return _descripcion; }
set { _descripcion = value; }
}
private int _Colores;
private bool _EntroPrimeraVez;
public bool EntroPrimeraVez
{
get { return _EntroPrimeraVez; }
set { _EntroPrimeraVez = value; notifyChanges("EntroPrimeraVez"); }
}
public int Colores
{
get { return _Colores; }
set { _Colores = value; notifyChanges("Colores"); }
}
}
It turned out the only thing I needed to do was passing the List explicitly as a list, like so:
ListaFechasPago = ListaFechasPago.ToList()
However, I seemed to have a mistake of concept in the way I was building the date fields. I ended up building as many registries as were needed of the same entry and binding a DateTime field to each, like so:
public static List<ClaseFechasPago> PintarFechas(ClaseFechasPago f)
{
List<ClaseFechasPago> ListaFechasPago = new List<ClaseFechasPago>();
for (int i = 0; i < f.numPagos; i++)
{
ClaseFechasPago fecha = new ClaseFechasPago();
fecha.cuotaInscripcion = 0M;
fecha.Inscripcion = true;
fecha.fechaPago = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day);
fecha.id = f.id;
fecha.descripcion = f.descripcion;
fecha.numPagos = f.numPagos;
fecha.Colores = f.Colores;
fecha.EntroPrimeraVez = f.EntroPrimeraVez;
ListaFechasPago.Add(fecha);
}
return ListaFechasPago;
//ListaFechasPago = ListaFechasPago.ToList();
}
Oh and of course initialize the ListaFechasPago List in the class that is set as DataContext for the window:
ListaFechasPago = new List<ClaseFechasPago>();
insde the class vmCursos_y_Diplomados
because I do:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = new vmCursos_y_Diplomados();
}

Categories

Resources