Binding object to dataset to create a report MvcReportViewer - model-view-controller

I'm trying to bind this object to a report RDLC:
public class DataReport
{
public string Name { get; set; }
public string[] DateArray { get; set; }
public DataReport()
{
this.DateArray = new string[5];
}
}
I want to pass this two property to the report using MvcReportViewr (here). I used his example but I couldn't find the way to pass this object. I previously create the datasource and the dataset that accepts the Name and DateArray property.
As explained in the example I passed them as class as in the code below. It did not work.
#using Microsoft.Reporting.WebForms;
#model DataReport
#using MvcReportViewer;
#{
ViewBag.Title = "MvcReportViewer Example";
}
#Html.MvcReportViewerFluent("ReportData.rdlc").ProcessingMode(ProcessingMode.Local).LocalDataSource("DataSet1", Model ).Attributes(new { Height = 900, Width = 900, style = "border: none" })
What is the best way to pass this object to the rdlc report?

Related

mvc dropdownlist not showing data value

I'm having an issue displaying data from the database into drop-downlist.
controller
TowinsEntities db = new TowinsEntities();
public ActionResult TMakes()
{
//T_Make make_db = new Models.T_Make();
ViewBag.carMaker = new SelectList(db.T_Make, "Make");
return View();
}
view
#Html.DropDownList("carMaker", "Select Make")
model
public partial class T_Make
{
public string Make { get; set; }
}
The output of a view is:
You need to overload your DropDownList with the string field names you want for value/display. You're only passing the model and selected value. I guess you'd use Make for both value and display (though, most people would use an ID for a value)
ViewBag.carMaker = new SelectList(db.T_Make, "Make","Make");
https://learn.microsoft.com/en-us/dotnet/api/system.web.mvc.selectlist.-ctor?view=aspnet-mvc-5.2#System_Web_Mvc_SelectList__ctor_System_Collections_IEnumerable_System_String_System_String_

Assigning Key from Kendo Grid row to main Model within a View

Using Kendo Grid in an MVC application.
The primary model for the View (PlanViewModel) contains a property which is a foreign key to another entity (BuildingId) For instance,
public class PlanViewModel
{
public int PlanId { get; set; }
public string PlanName { get; set; }
public int BuildingId { get; set; }
...
}
The Building ID is to be populated by the selected row of a BuildingGrid on the View.
So with the BuildingGrid, I'm using the .Selectable setting to invoke an onChange event. How in the following event would I update the model.BuildingId
function onChange(arg) {
var selected = $.map(this.select(), function (item) {
return $(item).text();
});
/// UPDATE MODEL BuildingId here
}
Thanks!
Figured it out. First, I associated an Html attribute to the model property like so:
#Html.HiddenFor(m => m.BuildingID, new {id = "Id"})
Then I added the following jquery script:
$('#BuildingGrid').click(function() {
var gview = $(this).data("kendoGrid");
var selectedItem = gview.dataItem(gview.select());
var BuildingId = selectedItem.BuildingId;
$("#Id").val(BuildingId);
});
That set the Model.BuildingId perfectly.
Thanks to a tip I got from here: set a value to model using jQuery

Single property not getting bound on HttpPost

I'm working on the first MVC3 project at our company, and I've hit a block. No one can seem to figure out what's going on.
I have a complex Model that I'm using on the page:
public class SpaceModels : List<SpaceModel> {
public bool HideValidation { get; set; }
[Required(ErrorMessage=Utilities.EffectiveDate + Utilities.NotBlank)]
public DateTime EffectiveDate { get; set; }
public bool DisplayEffectiveDate { get; set; }
}
In the Controller, I create a SpaceModels object with blank SpaceModels for when Spaces get combined (this would be the destination Space).
// Need a list of the models for the View.
SpaceModels models = new SpaceModels();
models.EffectiveDate = DateTime.Now.Date;
models.DisplayEffectiveDate = true;
models.Add(new SpaceModel { StoreID = storeID, SiteID = siteID, IsActive = true });
return View("CombineSpaces", models);
Then in the View, I am using that SpaceModels object as the Model, and in the form making a TextBox for the Effective Date:
#model Data.SpaceModels
#using (Html.BeginForm("CombineSpaces", "Space")) {
<div class="EditLine">
<span class="EditLabel LongText">
New Space Open Date
</span>
#Html.TextBoxFor(m => m.EffectiveDate, new {
size = "20",
#class = "datecontrol",
// Make this as a nullable DateTime for Display purposes so we don't start the Calendar at 1/1/0000.
#Value = Utilities.ToStringOrDefault(Model.EffectiveDate == DateTime.MinValue ? null : (DateTime?)Model.EffectiveDate, "MM/dd/yyyy", string.Empty)
})
#Html.ValidationMessageFor(m => m.EffectiveDate)
</div>
<hr />
Html.RenderPartial("_SpaceEntry", Model);
}
The Partial View that gets rendered iterates through all SpaceModels, and creates a containing the Edit fields for the individual SpaceModel objects. (I'm using the List to use the same Views for when the Spaces get Subdivided as well.)
Then on the HttpPost, the EffectiveDate is still back at it's DateTime.MinValue default:
[HttpPost]
public ActionResult CombineSpaces(SpaceModels model, long siteID, long storeID, DateTime? effectiveDate) {
// processing code
}
I added that DateTime? effectiveDate parameter to prove that the value when it gets changed does in fact come back. I even tried moving the rendering of the TextBox into the _SpaceEntry Partial View, but nothing worked there either.
I did also try using the #Html.EditorFor(m => m.EffectiveDate) in place of the #Html.TextBoxFor(), but that still returned DateTime.MinValue. (My boss doesn't like giving up the control of rendering using the #Html.EditorForModel by the way.)
There has to be something simple that I'm missing. Please let me know if you need anything else.
Looking at the source code for DefaultModelBinder, specifically BindComplexModel(), if it detects a collection type it will bind the individual elements but will not attempt to bind properties of the list object itself.
What model binding does is attempt to match the names of things or elements in the view to properties in your model or parameters in your action method. You do not have to pass all of those parameters, all you have to do is add them to your view model, then call TryUpdateModel in your action method. I am not sure what you are trying to do with SpaceModel or List but I do not see the need to inherit from the List. Im sure you have a good reason for doing it. Here is how I would do it.
The view model
public class SpacesViewModel
{
public DateTime? EffectiveDate { get; set; }
public bool DisplayEffectiveDate { get; set; }
public List<SpaceModel> SpaceModels { get; set; }
}
The GET action method
[ActionName("_SpaceEntry")]
public PartialViewResult SpaceEntry()
{
var spaceModels = new List<SpaceModel>();
spaceModels.Add(
new SpaceModel { StoreID = storeID, SiteID = siteID, IsActive = true });
var spacesVm = new SpacesViewModel
{
EffectiveDate = DateTime.Now,
DisplayEffectiveDate = true,
SpaceModels = spaceModels
};
return PartialView("_SpaceEntry", spacesVm);
}
The POST action method
[HttpPost]
public ActionResult CombineSpaces()
{
var spacesVm = new SpacesViewModel();
// this forces model binding and calls ModelState.IsValid
// and returns true if the model is Valid
if (TryUpdateModel(spacesVm))
{
// process your data here
}
return RedirectToAction("Index", "Home");
}
And the view
<label>Effective date: </label>
#Html.TextBox("EffectiveDate", Model.EffectiveDate.HasValue ?
Model.EffectiveDate.Value.ToString("MM/dd/yyyy") : string.empty,
new { #class = "datecontrol" })
Sometimes you need to explicitly bind form data using hidden fields such as
#Html.HiddenField("EffectiveDate", Model.EfectiveDate.)
In order to bind the properties of the SpaceModel object you can add individual properties such as SiteID to the view model or add a SpaceModel property for a single SpaceModel. If you want to successfully bind a complex model, add it as a Dictionary populated with key-value pairs rather than a List. You should then add the dictionary to the view model. You can even add a dictionary of dictionaries for hierarchical data.
I hope this helps :)

How can I utilise my custom DisplayTemplate with non-text fields so that it doesn't override existing values?

I've been following this guide on creating custom display attributes (specifically extra html attributes) to apply to the properties in my ViewModel. I have overridden both String and Boolean in the EditorTemplates folder. The editor template checks to see if a value has been set/the display attribute has been used - and adds the additional html attributes.
I'm getting stuck on the Boolean override when performing an edit action though. Regardless of whether or not I apply the attribute to a string, the ViewModel always maps with the correct existing data. This isn't true with any other form input type, due to the way the templates have been written by changing the type attribute inside a TextBoxFor.
I've been writing this primarily because I have been digging into knockout, and wanted an easy way to apply the data-bind attribute to strongly-typed views - if there's a better way please let me know!
Attribute Code:
public class Knockout : Attribute
{
public string DataBind { get; set; }
public string InputType { get; set; }
/*
Example:
Knockout("checked: showUploader", "checkbox")
Adds the HTML attributes data-bind="checked: showUploader" type="checkbox"
*/
public Knockout(string dataBind, string inputType)
{
this.DataBind = dataBind;
this.InputType = inputType;
}
public Dictionary<string, object> OptionalAttributes()
{
var options = new Dictionary<string, object>();
if(!string.IsNullOrWhiteSpace(DataBind))
{
options.Add("data-bind", DataBind);
}
if (!string.IsNullOrWhiteSpace(InputType))
{
options.Add("type", InputType);
}
return options;
}
}
Template Code
#using CumbriaMD.Infrastructure.ViewModels.DisplayAttributes
#{
var key = "Knockout";
}
#if (ViewData.ModelMetadata.AdditionalValues.ContainsKey(key))
{
var knockout = ViewData.ModelMetadata.AdditionalValues[key] as Knockout;
#Html.TextBoxFor(model => model, knockout.OptionalAttributes())
}
else
{
/*
When the attribute is not present, the default action is the following - which seems to
be overriding the data mapped from the database:
*/
#Html.TextBoxFor(model => model, new { type="checkbox" })
}
Found the answer nested in this beauty of a question!
My working template for boolean values now looks like:
#using CumbriaMD.Infrastructure.ViewModels.DisplayAttributes
#{
var key = "Knockout";
bool? value = null;
if(ViewData.Model != null)
{
value = Convert.ToBoolean(ViewData.Model, System.Globalization.CultureInfo.InvariantCulture);
}
}
#if (ViewData.ModelMetadata.AdditionalValues.ContainsKey(key))
{
var knockout = ViewData.ModelMetadata.AdditionalValues[key] as Knockout;
#Html.CheckBox("", value ?? false, knockout.OptionalAttributes())
}
else
{
#Html.CheckBox("", value ?? false, new { #class = "check-box" })
}

Reading Razor View Variables Programmatically

I have "widgets" contained within partial views and iterate over these files to build an available widget list. Within these views there are two variables
#{ ViewBag.Name = "Test Widget"; }
#{ ViewBag.Description = "This is a test widget"; }
self explanatory. Is there a way using RazorEngine (or any other way) to "read" these variables out so I can show the value of ViewBag.Description in my widget list?
Thanks.
RazorEngine with a custom base view could be used for this purpose:
public class MyViewModel
{
public string Name { get; set; }
public string Description { get; set; }
}
class Program
{
static void Main()
{
// don't ask me about this line :-)
// it's used to ensure that the Microsoft.CSharp assembly is loaded
bool loaded = typeof(Binder).Assembly != null;
Razor.SetTemplateBase(typeof(FooTemplateBase<>));
string template =
#"#{ ViewBag.Name = ""Test Widget""; }
#{ ViewBag.Description = ""This is a test widget""; }";
var model = new MyViewModel();
var result = Razor.Parse(template, model);
Console.WriteLine(model.Name);
Console.WriteLine(model.Description);
}
}
namespace System.Web.Mvc
{
public abstract class FooTemplateBase<T> : TemplateBase<T>
{
public dynamic ViewBag
{
get
{
return Model;
}
}
}
}
You can use the RazorGenerator to actually compile the views, then do a sample execution of them to be able to read the data.
See this article on unit testing the razor views, a similar thing may work for you:
http://blog.davidebbo.com/2011/06/unit-test-your-mvc-views-using-razor.html
var view = new Index();
// Set up the data that needs to be accessed by the view
view.ViewBag.Message = "Testing";
// Render it in an HtmlAgilityPack HtmlDocument. Note that
// you can pass a 'model' object here if your view needs one.
// Generally, what you do here is similar to how a controller
//action sets up data for its view.
HtmlDocument doc = view.RenderAsHtml();
//recall name and description
var name = view.ViewBag.Name;
var description = view.ViewBag.Description;
Here is some additional info on the Razor Generator from Phil Haack: http://haacked.com/archive/2011/08/01/text-templating-using-razor-the-easy-way.aspx

Resources