عنوان:

‫چگونگی گزارشگیری از Business Objects مانند List توسط StimulSoft


نویسنده: وریا کریمی
تاریخ: ۱۳۹۳/۰۳/۱۴ ۹:۱۰
آدرس: www.dntips.ir

می‌خواهیم از یک لیست در گزارش خود استفاده کنیم؛ بطور مثال وقتی در LINQ  از دستور ToList استفاده می‌کنیم و می‌خواهیم آنرا بصورت مستقیم به Stimul بفرستیم. فرض بر این است که شما DLLهای Stimul را به پروژه اضافه کرده اید و آماده گزارشگیری هستید.

مثلا مدلی در Entity FrameWork با نام base_CenterType 

public class base_CenterType
    {
        public int ID { get; set; }
        public string Title { get; set; }
        public string Dsc { get; set; }
     }

و متدی بصورت ذیل:

public IList<base_CenterType> GetAll()
        {
            return _base_CenterType.ToList();
        }

طراحی گزارش برای این لیست به این صورت است:

1- اضافه کردن StiWebReport به فرم به نام StiWebReport1

2- با کلیک بر روی فلش سمت راست و بالای StiWebReport1 و انتخاب Design Report، وارد قسمت طراحی می‌شویم:

3- با راست کلیک بر روی Business Object و انتخاب New Business Object  پنجره مربوطه باز میشود:

4- بعد از زدن OK پنجره زیر باز خواهد شد که باید در کادر Name نام Business Object را انتخاب کنیم که برای خوانایی بهتر است همان نام کلاس را برای آن انتخاب کنیم. چون Category  نداریم پس باید کادر آن خالی بماند. 

در قسمت Columns باید ستون‌های هم نام و هم نوع با خواص کلاس base_CenterType  را ایجاد کنیم. 

و نهایتا Business Objectی به نام base_CenterType با سه ستون ایجاد خواهد شد. 

  حال می‌توانید ستون‌های مورد نظر را در گزارش بکار ببرید.

با فرض اینکه گزارش را طراحی کرده و آنرا در ریشه درایو C ذخیره کرده‌اید، از  قطعه کد زیر برای ارسال لیست به گزارش و نمایش آن استفاده میکنیم.

StiReport mainreport = new StiReport();            
mainreport.RegBusinessObject("base_CenterType", base_CenterTypeService.GetAll());
mainreport.Load("C:\\StiWebReport2.mrt");
mainreport.Show();

نظرات

  • ژوپیتر در ۱۳۹۳/۰۳/۱۷ ۸:۴۷
    این روش بسیار زمانبر است؛ تصور کنید می‌خواهید از چندین جدول با تعداد فیلد‌های زیاد گزارش‌های مختلف تهیه کنید. اگر قرار به ایجاد دستی همه‌ی این فیلد‌ها در BO باشد که فاتحه وقت شما خوانده است و همچنین احتمال خطای شما در انتخاب نام و نوع فیلد بسیار خواهد بود.
    برای انجام بهتر این کار در Stimul Report باید ابتدا لیست جنریک مورد نظر را به گزارش ارسال کنیم و سپس آن را در حالت Design نمایش داده و پس از ایجاد عناصر گزارش آن را در محل مورد نظر ذخیره نماییم. (لیست ارسالی به صورت خودکار در گزارش‌ساز نشان داده خواهد شد و تنها کاری که باید کنید گرفتن و کشیدن فیلدهای مورد نظر به داخل Data-Header-Footer ریپورت است).
    برای مثال (با فرض استفاده از reportViewer):
    stiReportMain.Dictionary.Report.BusinessObjectsStore.Clear();
    reportViewer.Report.BusinessObjectsStore.Clear();
    stiReportMain.RegBusinessObject("MyCategory", "documents", list);
    stiReportMain.Design();
    (کد فوق فقط برای طراحی گزارش است و باید یکبار اجرا شود).
    پس از اجرای برنامه و طراحی گزارش مورد نظر آن را ذخیره می‌کنیم و برای استفاده کافی‌است از کد زیر استفاده کنیم:
    stiReportMain.Dictionary.Report.BusinessObjectsStore.Clear();
    reportViewer.Report.BusinessObjectsStore.Clear();
    stiReportMain.RegBusinessObject("MyCategory", "documents", list);
    stiReportMain.Dictionary.SynchronizeBusinessObjects();
    stiReportMain.Compile();
    stiReportMain.RenderWithWpf(true); //Or Render();
    نکته ۱: documents لیست جنریک دریافتی از EF یا هر ORM دیگست.
    نکته ۲:در صورتی که از شی StiReport استفاده کنید حتی نیاز به دادن آدرس فایل گزارش نخواهید داشت و فایل گزارش درون برنامه شما ذخیره خواهد شد. برای ویرایش آن نیز می‌توانید از فلش ظاهر شده کنار StiReport آن را ویرایش و مجددا ذخیره کنید.
    • مهمان در ۱۳۹۳/۰۷/۰۷ ۱۱:۲۷
      این طور که شما گفتید مسلما سرعت کار بالا می‌رود ولی انعطاف کار از دست می‌رود چون اگر هر تغییری در ORM بدهیم باید دوباره design کنیم .
  • زنده در ۱۳۹۴/۰۲/۲۴ ۱۸:۵۶
    اگر بخواهیم دو لیست را به یک گزارش ارسال کنیم چگونه باید کار کنم؟
    • علیرضا پایدار در ۱۳۹۴/۰۲/۲۵ ۱۲:۳۸
      این روش خیلی سرعت گزارش ساختن را افزایش میده منم قبلا یادمه از این روش استفاده می‌کردم

      StiReport report = new StiReport();
      var _context = new DBTestContext();
      var model = _context.Books.ToList();
      report.RegBusinessObject("Books", model);
      report.Dictionary.SynchronizeBusinessObjects();
      report.Dictionary.Synchronize();
      report.DesignWithWpf();
      کافی هست متد RegBusinessObject  را دوبار صدا بزنید. مثل اینجا
    • وریا کریمی در ۱۳۹۴/۰۳/۰۳ ۱۵:۱۹
      انعطاف stimul بسیار بالاست 
      بطور مثال:
      Stimulsoft.Report.StiReport stiReport = new Stimulsoft.Report.StiReport();
      stiReport.Load("Reports//YourReportName.mrt");
      برای ارسال یک یا چند لیست:
      stiReport.RegBusinessObject("Name1", List1);
      stiReport.RegBusinessObject("Name2", List2);
      stiReport.RegBusinessObject("Name3", List3);
      برای ارسال DataTable:
      DataSet ds = new DataSet();
      ds.Tables.Add(YourDataTable);
      stiReport.RegData(ds);
      برای ارسال متغییر:
      stiReport.Dictionary.Variables["Today"].Value = DateTime.Now.ToShortDateString();
      میتوان همه‌ی این ها(لیست، متغییر، DataTable) را باهم یکجا به stimul ارسال کرد.
      برای رفتن به حالت طراحی:
      stiReport.Design();
      برای نمایش گزارش:
      stiReport.Compile();
      stiReport.Render();
      stiViewer.Report = stiReport;
      stiViewer.Show();
  • بالازاده در ۱۳۹۵/۰۵/۳۰ ۹:۵۹
    آیا در استفاده به مشکل عدم برابر بودن طول و عرض صفحه گزارش در زمان چاپ ، با مقدار آن‌ها در کد یا فایل mrt برخورد کرده‌اید؟
    یعنی به طور مثال طول و عرض کاغذ را برابر اندازه A4 و نوع paper  را A4 قرار میدهیم ولی برای group کردن نتایج و قرار دادن new page before یا new page after، صفحات به درستی قرار نمیگیرند و  break صفحات به درستی کار نمی‌کند.  یا صفحات به سمت بالا کشیده می‌شوند (کوچتر بودن طول صفحه) و یا به سمت پایین کشیده می‌شوند (بلندتر بودن طول صفحه).
    به عنوان نمونه صفحات به سمت بالا کشیده می‌شوند (کوچتر بودن طول صفحه):

    • بالازاده در ۱۳۹۵/۰۶/۰۲ ۸:۵۴
      با تغییر viewer از StiMobileViewer به StiWebViewer مشکل برطرف شد. مثل این که یک باگ در StiMobileViewer است.
  • علی یگانه مقدم در ۱۳۹۵/۰۷/۱۴ ۰:۳۲
    من در حین اجرا خط نهایی Action Result  به خطای زیر برخورد می‌کنم
     Could not load file or assembly 'UsingBusinessObjects.dll' or one of its dependencies. The system cannot find the file specified.

    غیر از این فایل ها باید چیز دیگری هم بار اضافه کنم؟
    • علی یگانه مقدم در ۱۳۹۵/۰۷/۱۴ ۲:۵۰
      مشکل حل شد
      مشکل از فایل گزارش بود. فایل گزارش رو در خود استادیو stimul باز کردم و در حالت preview قرار دادم و موقع کمپایل با خطا روبرو شد و متوجه شدم که مشکل از فایل mrt بود.
  • احمد نهازی در ۱۳۹۵/۱۱/۱۸ ۱۲:۱۸
    سلام
    یکی از مشکلاتی که در طراحی با نرم افزار Designer از گزارش ساز StimulSoft داشتم، قرار دادن چند جدول یا  Business Object‌های مختلف در یک ردیف است.
    یکی از راه حل‌های موجود، استفاده  از panel است که توضیح آن به شرح ذیل می‌باشد.
    امیدوارم این راه حل بتونه برای شما مفید واقع باشه.

    ابتدا برای نمونه چند Business Object  مطابق شکل زیر ایجاد کنید:


    سپس با استفاده از پنل‌های تو در تو، این Business Object‌ها یا جداول را در کنار هم قرار می‌دهیم.


    فقط دقت کنید که گزینه‌های Can Grow و  Grow To Height فعال باشند تا در صورت وجود اطلاعات بیشتر در جدوال، تمام اطلاعات را نشان دهد.


    در نهایت جداول یا business object‌ها را در پنل‌های مربوطه قرار می‌دهیم.


    موفق و موید باشید 

  • مجید بصیرتی در ۱۳۹۵/۱۲/۰۲ ۹:۲
    سلام
    من یه بیزینس آبجکت دارم که درونش چندتا بیزینس آبجکت دیگه هست

    هر بیزینس آبجکت داخلی رو بین یک GroupHeader و GroupFooter قرار دادم. من این بیزینس آبجکتهای داخلی رو با درگ کردن درون محیط دیزاین قرار دادم. مشکلی که وجود داره اینه که وقتی خروجی گزارش توی برنامه تکراری هست. یعنی اینکه اطلاعات بیزینس آبجکتهای LanguageCourses و... چند بار توی خروجی تکرار میشه. مشکل هم از پاس دادن اطلاعات توی اکشن نیست. اونجا اطلاعات درست پاس داده میشه. و جالب اینجاست که مثلا اگر من فقط دوتا بیزینس آبجکت LanguageCourses و Education رو توی گزارش بذارم اطلاعات این دوتا، دوبار تکرار میشه. اگه یه بیزینس آبجکت دیگه (مثلا TrainingCourses) رو هم بیارم تو گزارش، اطلاعات هر کدوم از این سه تا، سه بار تکرار میشه!

    چند روز همین مشکل منو درگیر کرده و هنوز هم به نتیجه ای نرسیدم.

    ممنون میشم دوستان راهنمایی کنند.

    فایل گزارش: Report-9b7bd75b5286457a96f25f7ed37e45b6.mrt 

    • بالازاده در ۱۳۹۵/۱۲/۰۲ ۱۱:۲۸
      با سلام
      نمونه ارسالی شما با سوال مطرح شده از لحاظ business object و GroupHeader و GroupFooter هماهنگی ندارد. لطفا نمونه دقیقتری برای بررسی ارسال کنید.
      در ضمن condition‌های GroupHeader شما چیست؟
    • بالازاده در ۱۳۹۵/۱۲/۰۲ ۱۱:۵۱
      با کد زیر
          public class Book
          {
              public string Title { get; set; }
              public int Id { get; set; }
              public Author Author { get; set; }
              public Publisher Publisher { get; set; }
      
              public IList<Book> GetBooks()
              {
                  var books = new List<Book>();
      
                  for (int i = 0; i < 10; i++)
                  {
                      var book = new Book()
                      {
                          Id = i,
                          Title = $"Title {i}",
                          Author = new Author()
                          {
                              FirstName = $"Author {i} First Name",
                              LastName = $"Author {i} Last Name",
                              Person = new Person()
                              {
                                  Type = $"Type {i}",
                                  Value = $"Value {i}"
                              }
                          },
                          Publisher = new Report.Publisher()
                          {
                              Name = $"Publiser {i}"
                          }
                      };
                      books.Add(book);
                  }
                  return books;
              }
          }
      
          public class Author
          {
              public string FirstName { get; set; }
              public string LastName { get; set; }
              public Person Person { get; set; }
          }
      
          public class Person
          {
              public string Type { get; set; }
              public string Value { get; set; }
          }
      
          public class Publisher
          {
              public string Name { get; set; }
          }
      و فایل mrt زیر مشکلی دیده نشد.
  • محمدحسن سلمانیان در ۱۳۹۸/۰۹/۰۷ ۱۳:۲
    آیا میشه در یک فایل mrt از چندین Business Object استفاده کرد و هر کدام از آنها در چند صفحه متفاوت نمایش داده بشه و در C# چگونه اون صفحه را بهش بگیم بیاره مثله اکسل که در یک فایل از چند Sheet استفاده شده.
    • بالازاده در ۱۳۹۸/۰۹/۰۷ ۱۳:۵۶
      بله امکانپذیر است.
      برای افزودن صفحه جدید در قسمت صفحات راست کلیک کرده و صفحه جدیدی اضافه کنید.

      در اینجا امکان حذف صفحات و تغییر نام آن‌ها نیز وجود دارد.
      نکته‌ای که باید درنظر داشته باشید این است که در زمان چاپ ابتدا صفحات صفحه‌ی اول! چاپ شده و بعد صفحات صفحه دوم. یعنی اگر در مثال بالا لیست مشتریان 4 صفحه و لیست محصولات 3 صفحه باشد ابتدا 4 صفحه مشتریان چاپ شده و سپس 3 صفحه محصولات.
      در قسمت Business Objects می‌توانید چندین Business Object تعریف کنید و از آن‌ها به عنوان دیتای چند DataBand استفاده کنید.
      صفحه مشتریان:

      و صفحه محصولات:

      فایل مثال فوق MultiPageReport.mrt .
      • محمدحسن سلمانیان در ۱۳۹۸/۰۹/۰۸ ۱۶:۳۷
        اگر بخواهم فقط صفحه ProductPage را به کاربر نمایش بدهد باید چه کاری انجام بدهم؟
        چون من همین کار را کردم اما در خروجی که می‌آورد همه صفحات را می‌آورد یعنی Buissines object مشتریان را پر میکنم و بهش آدرس فایل را میدهم همه اطلاعات را می‌آورد و صفحه Product Page را هم نمایش میدهد من میخواهم در یکجا فقط مثلا لیست مشتریان را به صورت خروجی به کاربر نمایش بدهد و در جای دیگر فقط اطلاعات (صفحه) محصولات را به کاربر نمایش بدهد و این کار در فایل‌های جدا امکان پذیر است؟ حالا سوالی که دارم این است آیا میشه در یک فایل چنین کاری کرد یا خیر؟
        اگر جواب بله است کد C# آن به چه صورت نوشته میشود.
        چون کدی که به صورت یک صفحه ای باشد و اطلاعات آن Bussisnes Object را بیاورد به این صورت است و دارم.
         try
        {
               var mainReport = new StiReport();
        
               var details = new List<Details>();
        
               foreach (var item in Items)
               {
                        details.Add(new Details
                        {
                            CarName = item.CarName,
                        });
               }
        
               mainReport.Load(CurrentDirectory() + @"\Rp.dll");
               mainReport.RegBusinessObject("Print", details);
               mainReport.Show();
         }
         catch (Exception ex)
         {
               MessageUtility.ErrorAlert(ex.GetOriginalException().Message);
         }
        • بالازاده در ۱۳۹۸/۰۹/۰۹ ۱۰:۳۳
          برای عدم نمایش یک صفحه از کد زیر استفاده کنید:
          mainReport.Pages[0].Enabled = false;
          mainReport.Pages["PageName"].Enabled = false;
          ولی این کار شیوه درستی نیست.
          بهتر است برای هر گزارش یک فایل mrt درست کنید و در هنگام گزارش‌گیری با توجه به نوع گزارش، فایل mrt مورد نظر را لود کرده و اطلاعات مورد نیاز را به آن ارسال کنید.
          در مثال بالا می‌توانید دو فایل ProductsReport.mrt و CustomersReport.mrt تولید کنید و در هر کدام BusinessObject مورد استفاده خود گزارش را تولید کنید و با توجه به شرط خود فایل مورد نظر را لود  کنید:
          var mrtName = "";
          object businessObject = new object();
          if (cusomerReportCondition)
          {
              mrtName = "CustomersReport.mrt";
              businessObject = getCustomerReportData();
          }
          else if (productsReportCondition)
          {
              mrtName = "ProductsReport.mrt";
              businessObject = getProductsReportData();
          }
          //else if ...
          
          mainReport.Load(mrtName);
          mainReport.RegBusinessObject("businessObjectName", businessObject);
          به عنوان یک Best Practice بهتر است برای جمع آوری اطلاعات هر گزارش، یک فایل cs جداگانه تهیه کنید به طور مثال ProductsReport.cs و CustomersReport.cs؛ و تمام فایل‌های cs گزارش خود را به یک پروژه‌ی دیگر مانند ProjectName.Reports انتقال دهید.
          • محمدحسن سلمانیان در ۱۳۹۸/۰۹/۰۹ ۱۲:۳
            تنها دلیلی که از این راه استفاده میکنم اینه که اگه از طرف کاربر فایل پاک شد من میتونم فایل پرینتی که دارم را آپلود کنم و اونو با آپدیتی که به کاربر میدهم در مکانی که برای پرینت استفاده میشه بگذارم و همین طور اگه اطلاعات تغییر کرد هم به همین شکل باشد.
            اما اگر در فایل‌های مختلف باشد باید کاربر با پشتیبانی تماس بگیره و بگه در کدوم فرم خطای مورد نظر را میده. این بهترین راه حلی بود که به ذهنم رسیده است و ممنون بابت جواب مشکل حل شد.
            • بالازاده در ۱۳۹۸/۰۹/۰۹ ۱۲:۴۶
              روشی که خود من در برنامه استفاده می‌کنم این است که تمام فایل‌های mrt گزارشات را در یک پوشه قرار داده‌ام و یک viewer برای تمام پروژه دارم که با ارسال ReportId - شناسه گزارش مورد نظر- به آن صفحه، نوع گزارش، فایل mrt آن و دیتای آنرا مشخص کرده و گزارش را نمایش می‌دهم.
               روش شما شاید در تعداد گزارشات محدود براحتی قابل مدیریت باشد ولی زمانی که تعداد گزارشات بیشتر شود، مدیریت آن سخت خواهد شد؛ به ویژه که بخواهید گزارش داینامیک تولید کنید.
              پ.ن: در این سایت برای تشکر و ابراز علاقه‌مندی نسبت به مطلب یا پاسخی از سیستم امتیازدهی استفاده می‌شود؛ می‌توانید از آن استفاده کنید.
  • هادی مزارعی در ۱۴۰۴/۰۴/۰۷ ۱۳:۲۳
    در نسخه جدید از Stimul امکان استفاده از مدل‌های ایجاد شده (DTO) در یک پروژه ClassLibrary وجود ندارد و باید Assembly در زمان کامپایل، با نسخه 4.8 کامپایل شود. اگر امکان ایجاد Assembly بصورت MultiTarget وجود نداشت بهترین راه ایجاد یک فایل Json از کلاس‌ها و Propertyهای موجود در آن است. برنامه‌های Telerik و DevExpress هم همین مشکل را دارند و در نهایت به استفاده از Json به عنوان مبنای طراحی داده‌های قابل نمایش در یک گزارش روی آوردم.
    • بالازاده در ۱۴۰۴/۰۴/۰۷ ۱۵:۵۸
      ممکن است مشخص کنید منظور از نسخه جدید، کدام نسخه هست؟ مشخصات فریمورک مورد استفاده پروژه اصلی و پروژه‌های جانبی که از Stimul استفاده می‌کنند به چه صورت هست؟
      آیا منظور شما عدم نمایش Propertyهای کلاس DTO در قسمت Business Object در زمان طراحی فایل mrt در دیزاینر Visul Studio است یا در برنامه StimulSoft Designer؟
      • بالازاده در ۱۴۰۴/۰۴/۰۸ ۱۳:۰۵
        فرض را بر استفاده از StimulSoft Standalone Designer می‌گذاریم. همچنین در تاریخ ارسال این متن، دو نسخه‌ی به‌طور نسبی جدید در ایران را در نظر می‌گیریم: 2023.1.1 و 2024.3.2.

        در قسمت About برنامه‌ی Designer امکان مشاهده‌ی جزییات بیشتری درباره‌ی آن داریم. همانطور که در تصویر زیر مشخص است که Designer نسخه‌ی 2023.1.1 (نصب شده بر روی سیستم) بر پایه ‎.NET 4.7.2 است


        که در وارد کردن مدل‌ها برای BusinessObjects از پروژه‌هایی بر پایه ‎.NET 8 خطای زیر را صادر می‌کند:
        Unable to load one or more requested types. Retrieve the LoaderExceptions property for more information.
        با جزییات بیشتر زیر:
        Unable to load one or more of the requested types: Could not load file or assembly 'System.Runtime, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.

        برای حل مشکل فوق در ساخت فایل mrt گزارش، در نسخه‌های جدید ‎.NET (به‌طور مثال ‎.NET 8)، به‌عنوان یک راه حل میانبر می‌توان به‌صورت زیر عمل کرد:
        1. ابتدا یک پروژه Windows Forms App بر پایه ‎.NET 8 ایجاد کنید.
        2. کتابخانه Stimulsoft.Reports.Net 2024.3.2 یا بالاتر را نصب کنید.
        3. در فرم ویندوز برنامه با کلیک بر روی یک دکمه و اجرای کد زیر، Designer را نمایش داده و از آن استفاده کنید:
        var report = new StiReport();
        report.Design();
        در قسمت About، مشخصات زیر قابل مشاهده است که Designer بر پایه ‎.NET 8.0 اجرا شده است:


        در قسمت Business Objects، مدل‌ها را از Class Library مورد نظر در پروژه اصلی وارد کرده و فایل گزارش را ذخیره و در پروژه اصلی استفاده کنید.


        همچنین اگر مدل‌ها در این پروژه فرعی در دسترس باشند، می‌توان در هنگام نمایش Designer، مدل را به صورت اتوماتیک با عمق مورد نظر در Business Objects تولید کرد. فرض کنیم مدل‌ گزارش یک سفارش به صورت زیر باشد:

        namespace Models;
        
        public class Order
        {
            public int OrderId { get; set; }
            public string CustomerName { get; set; }
            public List<OrderItem> Items { get; set; } = new();
        }
        
        public class OrderItem
        {
            public int Quantity { get; set; }
            public Product Product { get; set; } = new();
        }
        
        public class Product
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public decimal Price { get; set; }
        }

        با استفاده از کد زیر به نتیجه مورد نظر دست پیدا می‌کنیم:

        var report = new StiReport();
        report.RegBusinessObject("Model", new Order());
        report.Dictionary.SynchronizeBusinessObjects(6); // عمق 6
        report.Design();
        که در تصویر زیر مشخص است:


        برای اطلاعات بیشتر در مورد عمق مدل، این پاسخ را بررسی کنید.
      • هادی مزارعی در ۱۴۰۴/۰۴/۰۸ ۱۷:۲۴
        موردی که گفتم، در زمان طراحی گزارش خارج از Visual Studio و مستقیما توسط Designer بوده. با این حال اگر فایل Json به کسی کمک میکنه میتونید از ClassToJson که در دسترس هست استفاده کنید. این پروژه یک Nuget پکیج داره که برای راحتی کار یک UI با Windows Form براش ساختم. این برنامه نتیجه را در یک فایل JSON تحویل میده.